Fix performance regressions in large multi-project builds. (#571)
* Perf improvements to multi-project builds. * Fixes. * More fixes. * Layered mappings fixes * Perf improvements. Undo broken fix. * Fix remap classpath being empty. * Another gradle bug? Either way this is fine and works. * Fix broken test * Final fixes? * Fix and cleanup mixin ap mappings.
This commit is contained in:
		
							parent
							
								
									6fd3d5d021
								
							
						
					
					
						commit
						d40241d75a
					
				
					 36 changed files with 1179 additions and 416 deletions
				
			
		
							
								
								
									
										20
									
								
								build.gradle
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								build.gradle
									
									
									
									
									
								
							|  | @ -114,14 +114,8 @@ jar { | |||
| 	from configurations.bootstrap.collect { it.isDirectory() ? it : zipTree(it) } | ||||
| } | ||||
| 
 | ||||
| task sourcesJar(type: Jar, dependsOn: classes) { | ||||
| 	archiveClassifier = 'sources' | ||||
| 	from sourceSets.main.allSource | ||||
| } | ||||
| 
 | ||||
| task javadocJar(type: Jar, dependsOn: javadoc) { | ||||
| 	archiveClassifier = 'javadoc' | ||||
| 	from javadoc.destinationDir | ||||
| java { | ||||
| 	withSourcesJar() | ||||
| } | ||||
| 
 | ||||
| spotless { | ||||
|  | @ -197,10 +191,7 @@ publishing { | |||
| 			artifactId project.archivesBaseName | ||||
| 			version project.version | ||||
| 
 | ||||
| 			from components['java'] | ||||
| 
 | ||||
| 			artifact sourcesJar | ||||
| 			artifact javadocJar | ||||
| 			from components.java | ||||
| 
 | ||||
| 			pom.withXml { | ||||
| 				patchPom(asNode()) | ||||
|  | @ -213,10 +204,7 @@ publishing { | |||
| 			artifactId project.archivesBaseName | ||||
| 			version baseVersion + '-SNAPSHOT' | ||||
| 
 | ||||
| 			from components['java'] | ||||
| 
 | ||||
| 			artifact sourcesJar | ||||
| 			artifact javadocJar | ||||
| 			from components.java | ||||
| 
 | ||||
| 			pom.withXml { | ||||
| 				patchPom(asNode()) | ||||
|  |  | |||
|  | @ -24,7 +24,6 @@ | |||
| 
 | ||||
| package net.fabricmc.loom; | ||||
| 
 | ||||
| import java.io.File; | ||||
| import java.nio.file.Path; | ||||
| import java.util.List; | ||||
| import java.util.function.Supplier; | ||||
|  | @ -37,7 +36,6 @@ import org.gradle.api.Project; | |||
| import org.gradle.api.artifacts.Configuration; | ||||
| import org.gradle.api.file.ConfigurableFileCollection; | ||||
| import org.gradle.api.file.FileCollection; | ||||
| import org.gradle.api.tasks.SourceSet; | ||||
| 
 | ||||
| import net.fabricmc.loom.api.LoomGradleExtensionAPI; | ||||
| import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; | ||||
|  | @ -112,10 +110,6 @@ public interface LoomGradleExtension extends LoomGradleExtensionAPI { | |||
| 
 | ||||
| 	FileCollection getMinecraftJarsCollection(MappingsNamespace mappingsNamespace); | ||||
| 
 | ||||
| 	File getMixinMappings(SourceSet sourceSet); | ||||
| 
 | ||||
| 	FileCollection getAllMixinMappings(); | ||||
| 
 | ||||
| 	boolean isRootProject(); | ||||
| 
 | ||||
| 	default String getIntermediaryUrl(String minecraftVersion) { | ||||
|  |  | |||
|  | @ -37,7 +37,7 @@ public abstract class DecompilerOptions implements Named { | |||
| 	/** | ||||
| 	 * Class name for to the {@link LoomDecompiler}. | ||||
| 	 */ | ||||
| 	public abstract Property<String> getDecompilerClassname(); | ||||
| 	public abstract Property<String> getDecompilerClassName(); | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Additional classpath entries for the decompiler jvm. | ||||
|  | @ -60,7 +60,7 @@ public abstract class DecompilerOptions implements Named { | |||
| 	public abstract Property<Integer> getMaxThreads(); | ||||
| 
 | ||||
| 	public DecompilerOptions() { | ||||
| 		getDecompilerClassname().finalizeValueOnRead(); | ||||
| 		getDecompilerClassName().finalizeValueOnRead(); | ||||
| 		getClasspath().finalizeValueOnRead(); | ||||
| 		getOptions().finalizeValueOnRead(); | ||||
| 		getMemory().convention(4096L).finalizeValueOnRead(); | ||||
|  | @ -71,9 +71,9 @@ public abstract class DecompilerOptions implements Named { | |||
| 	public record Dto(String className, Map<String, String> options, int maxThreads) implements Serializable { } | ||||
| 
 | ||||
| 	public Dto toDto() { | ||||
| 		Preconditions.checkArgument(getDecompilerClassname().isPresent(), "No decompiler classname specified for decompiler: " + getName()); | ||||
| 		Preconditions.checkArgument(getDecompilerClassName().isPresent(), "No decompiler classname specified for decompiler: " + getName()); | ||||
| 		return new Dto( | ||||
| 				getDecompilerClassname().get(), | ||||
| 				getDecompilerClassName().get(), | ||||
| 				getOptions().get(), | ||||
| 				getMaxThreads().get() | ||||
| 		); | ||||
|  |  | |||
|  | @ -25,13 +25,14 @@ | |||
| package net.fabricmc.loom.api.mappings.layered; | ||||
| 
 | ||||
| import java.nio.file.Path; | ||||
| import java.util.function.Supplier; | ||||
| 
 | ||||
| import org.gradle.api.artifacts.Dependency; | ||||
| import org.gradle.api.logging.Logger; | ||||
| import org.jetbrains.annotations.ApiStatus; | ||||
| 
 | ||||
| import net.fabricmc.loom.configuration.providers.mappings.MappingsProvider; | ||||
| import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider; | ||||
| import net.fabricmc.mappingio.tree.MemoryMappingTree; | ||||
| 
 | ||||
| @ApiStatus.Experimental /* Very Experimental and not cleanly separated from the impl atm */ | ||||
| public interface MappingContext { | ||||
|  | @ -39,7 +40,7 @@ public interface MappingContext { | |||
| 
 | ||||
| 	Path resolveMavenDependency(String mavenNotation); | ||||
| 
 | ||||
| 	MappingsProvider mappingsProvider(); | ||||
| 	Supplier<MemoryMappingTree> intermediaryTree(); | ||||
| 
 | ||||
| 	MinecraftProvider minecraftProvider(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -42,6 +42,7 @@ import org.gradle.api.tasks.SourceSet; | |||
| import net.fabricmc.loom.LoomGradleExtension; | ||||
| import net.fabricmc.loom.configuration.ide.idea.IdeaUtils; | ||||
| import net.fabricmc.loom.extension.MixinExtension; | ||||
| import net.fabricmc.loom.task.service.MixinMappingsService; | ||||
| import net.fabricmc.loom.util.Constants; | ||||
| 
 | ||||
| /** | ||||
|  | @ -84,7 +85,7 @@ public abstract class AnnotationProcessorInvoker<T extends Task> { | |||
| 			String refmapName = Objects.requireNonNull(MixinExtension.getMixinInformationContainer(sourceSet)).refmapNameProvider().get(); | ||||
| 			Map<String, String> args = new HashMap<>() {{ | ||||
| 					put(Constants.MixinArguments.IN_MAP_FILE_NAMED_INTERMEDIARY, loom.getMappingsProvider().tinyMappings.toFile().getCanonicalPath()); | ||||
| 					put(Constants.MixinArguments.OUT_MAP_FILE_NAMED_INTERMEDIARY, loom.getMixinMappings(sourceSet).getCanonicalPath()); | ||||
| 					put(Constants.MixinArguments.OUT_MAP_FILE_NAMED_INTERMEDIARY, MixinMappingsService.getMixinMappingFile(project, sourceSet).getCanonicalPath()); | ||||
| 					put(Constants.MixinArguments.OUT_REFMAP_FILE, getRefmapDestination(task, refmapName)); | ||||
| 					put(Constants.MixinArguments.DEFAULT_OBFUSCATION_ENV, "named:intermediary"); | ||||
| 				}}; | ||||
|  |  | |||
|  | @ -193,15 +193,15 @@ public final class CompileConfiguration { | |||
| 
 | ||||
| 		boolean split = project.getProperties().get("fabric.loom.experimental.splitMcJars") != null; | ||||
| 
 | ||||
| 		// Provide the vanilla mc jars | ||||
| 		// Provide the vanilla mc jars -- TODO share across projects. | ||||
| 		final MinecraftProvider minecraftProvider = split ? new SplitMinecraftProvider(project) : new MergedMinecraftProvider(project); | ||||
| 		extension.setMinecraftProvider(minecraftProvider); | ||||
| 		minecraftProvider.provide(); | ||||
| 
 | ||||
| 		// Provide the mappings | ||||
| 		final MappingsProviderImpl mappingsProvider = new MappingsProviderImpl(project, minecraftProvider); | ||||
| 		final DependencyInfo mappingsDep = DependencyInfo.create(project, Constants.Configurations.MAPPINGS); | ||||
| 		final MappingsProviderImpl mappingsProvider = MappingsProviderImpl.getInstance(project, mappingsDep, minecraftProvider); | ||||
| 		extension.setMappingsProvider(mappingsProvider); | ||||
| 		mappingsProvider.provide(); | ||||
| 		mappingsProvider.applyToProject(project, mappingsDep); | ||||
| 
 | ||||
| 		// Provide the remapped mc jars | ||||
| 		final IntermediaryMinecraftProvider<?> intermediaryMinecraftProvider; | ||||
|  |  | |||
|  | @ -26,6 +26,7 @@ package net.fabricmc.loom.configuration.providers.mappings; | |||
| 
 | ||||
| import java.io.File; | ||||
| import java.nio.file.Path; | ||||
| import java.util.function.Supplier; | ||||
| 
 | ||||
| import org.gradle.api.Project; | ||||
| import org.gradle.api.artifacts.Configuration; | ||||
|  | @ -36,6 +37,7 @@ import org.gradle.api.logging.Logger; | |||
| import net.fabricmc.loom.LoomGradleExtension; | ||||
| import net.fabricmc.loom.api.mappings.layered.MappingContext; | ||||
| import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider; | ||||
| import net.fabricmc.mappingio.tree.MemoryMappingTree; | ||||
| 
 | ||||
| public class GradleMappingContext implements MappingContext { | ||||
| 	private final Project project; | ||||
|  | @ -62,8 +64,8 @@ public class GradleMappingContext implements MappingContext { | |||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public MappingsProvider mappingsProvider() { | ||||
| 		return extension.getMappingsProvider(); | ||||
| 	public Supplier<MemoryMappingTree> intermediaryTree() { | ||||
| 		return () -> IntermediaryService.getInstance(project, minecraftProvider()).getMemoryMappingTree(); | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
|  |  | |||
|  | @ -0,0 +1,118 @@ | |||
| /* | ||||
|  * 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.mappings; | ||||
| 
 | ||||
| import java.io.BufferedReader; | ||||
| import java.io.File; | ||||
| import java.io.IOException; | ||||
| import java.io.UncheckedIOException; | ||||
| import java.net.URL; | ||||
| import java.nio.charset.StandardCharsets; | ||||
| import java.nio.file.Files; | ||||
| import java.nio.file.Path; | ||||
| import java.util.Collections; | ||||
| import java.util.Objects; | ||||
| import java.util.function.Supplier; | ||||
| 
 | ||||
| import com.google.common.base.Suppliers; | ||||
| import com.google.common.net.UrlEscapers; | ||||
| import org.gradle.api.Project; | ||||
| import org.jetbrains.annotations.VisibleForTesting; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
| 
 | ||||
| import net.fabricmc.loom.LoomGradleExtension; | ||||
| import net.fabricmc.loom.LoomGradlePlugin; | ||||
| import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; | ||||
| import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider; | ||||
| import net.fabricmc.loom.util.DownloadUtil; | ||||
| import net.fabricmc.loom.util.service.SharedService; | ||||
| import net.fabricmc.loom.util.service.SharedServiceManager; | ||||
| import net.fabricmc.mappingio.adapter.MappingNsCompleter; | ||||
| import net.fabricmc.mappingio.format.Tiny2Reader; | ||||
| import net.fabricmc.mappingio.tree.MemoryMappingTree; | ||||
| 
 | ||||
| public final class IntermediaryService implements SharedService { | ||||
| 	private static final Logger LOGGER = LoggerFactory.getLogger(IntermediaryService.class); | ||||
| 
 | ||||
| 	private final Path intermediaryTiny; | ||||
| 	private final Supplier<MemoryMappingTree> memoryMappingTree = Suppliers.memoize(this::createMemoryMappingTree); | ||||
| 
 | ||||
| 	private IntermediaryService(Path intermediaryTiny) { | ||||
| 		this.intermediaryTiny = intermediaryTiny; | ||||
| 	} | ||||
| 
 | ||||
| 	public static synchronized IntermediaryService getInstance(Project project, MinecraftProvider minecraftProvider) { | ||||
| 		final LoomGradleExtension extension = LoomGradleExtension.get(project); | ||||
| 		final String encodedMinecraftVersion = UrlEscapers.urlFragmentEscaper().escape(minecraftProvider.minecraftVersion()); | ||||
| 		final String intermediaryArtifactUrl = extension.getIntermediaryUrl(encodedMinecraftVersion); | ||||
| 
 | ||||
| 		return SharedServiceManager.get(project).getOrCreateService("IntermediaryService:" + intermediaryArtifactUrl, | ||||
| 				() -> create(intermediaryArtifactUrl, minecraftProvider)); | ||||
| 	} | ||||
| 
 | ||||
| 	@VisibleForTesting | ||||
| 	public static IntermediaryService create(String intermediaryUrl, MinecraftProvider minecraftProvider) { | ||||
| 		final Path intermediaryTiny = minecraftProvider.file("intermediary-v2.tiny").toPath(); | ||||
| 
 | ||||
| 		if (!Files.exists(intermediaryTiny) || LoomGradlePlugin.refreshDeps) { | ||||
| 			// Download and extract intermediary | ||||
| 			File intermediaryJar = minecraftProvider.file("intermediary-v2.jar"); | ||||
| 
 | ||||
| 			try { | ||||
| 				DownloadUtil.downloadIfChanged(new URL(intermediaryUrl), intermediaryJar, LOGGER); | ||||
| 				MappingsProviderImpl.extractMappings(intermediaryJar.toPath(), intermediaryTiny); | ||||
| 			} catch (IOException e) { | ||||
| 				throw new UncheckedIOException("Failed to download and extract intermediary", e); | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		return new IntermediaryService(intermediaryTiny); | ||||
| 	} | ||||
| 
 | ||||
| 	private MemoryMappingTree createMemoryMappingTree() { | ||||
| 		final MemoryMappingTree tree = new MemoryMappingTree(); | ||||
| 
 | ||||
| 		try { | ||||
| 			MappingNsCompleter nsCompleter = new MappingNsCompleter(tree, Collections.singletonMap(MappingsNamespace.NAMED.toString(), MappingsNamespace.INTERMEDIARY.toString()), true); | ||||
| 
 | ||||
| 			try (BufferedReader reader = Files.newBufferedReader(getIntermediaryTiny(), StandardCharsets.UTF_8)) { | ||||
| 				Tiny2Reader.read(reader, nsCompleter); | ||||
| 			} | ||||
| 		} catch (IOException e) { | ||||
| 			throw new UncheckedIOException("Failed to read intermediary mappings", e); | ||||
| 		} | ||||
| 
 | ||||
| 		return tree; | ||||
| 	} | ||||
| 
 | ||||
| 	public MemoryMappingTree getMemoryMappingTree() { | ||||
| 		return memoryMappingTree.get(); | ||||
| 	} | ||||
| 
 | ||||
| 	public Path getIntermediaryTiny() { | ||||
| 		return Objects.requireNonNull(intermediaryTiny, "Intermediary mappings have not been setup"); | ||||
| 	} | ||||
| } | ||||
|  | @ -28,107 +28,135 @@ import java.io.BufferedReader; | |||
| import java.io.File; | ||||
| import java.io.IOException; | ||||
| import java.io.Reader; | ||||
| import java.net.URL; | ||||
| import java.io.UncheckedIOException; | ||||
| import java.nio.charset.StandardCharsets; | ||||
| import java.nio.file.FileSystem; | ||||
| import java.nio.file.FileSystems; | ||||
| import java.nio.file.Files; | ||||
| import java.nio.file.Path; | ||||
| import java.nio.file.StandardCopyOption; | ||||
| import java.util.Arrays; | ||||
| import java.util.Collections; | ||||
| import java.util.Map; | ||||
| import java.util.Objects; | ||||
| import java.util.regex.Pattern; | ||||
| import java.util.function.Supplier; | ||||
| 
 | ||||
| import com.google.common.base.Stopwatch; | ||||
| import com.google.common.net.UrlEscapers; | ||||
| import com.google.common.base.Suppliers; | ||||
| import com.google.gson.JsonObject; | ||||
| import org.apache.tools.ant.util.StringUtils; | ||||
| import org.gradle.api.Project; | ||||
| import org.jetbrains.annotations.Nullable; | ||||
| import org.objectweb.asm.Opcodes; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
| 
 | ||||
| import net.fabricmc.loom.LoomGradleExtension; | ||||
| import net.fabricmc.loom.LoomGradlePlugin; | ||||
| import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; | ||||
| import net.fabricmc.loom.configuration.DependencyInfo; | ||||
| 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; | ||||
| import net.fabricmc.loom.util.DownloadUtil; | ||||
| import net.fabricmc.loom.util.ZipUtils; | ||||
| import net.fabricmc.loom.util.service.SharedService; | ||||
| import net.fabricmc.loom.util.service.SharedServiceManager; | ||||
| import net.fabricmc.mappingio.MappingReader; | ||||
| import net.fabricmc.mappingio.adapter.MappingNsCompleter; | ||||
| import net.fabricmc.mappingio.adapter.MappingSourceNsSwitch; | ||||
| import net.fabricmc.mappingio.format.MappingFormat; | ||||
| import net.fabricmc.mappingio.format.Tiny2Reader; | ||||
| import net.fabricmc.mappingio.format.Tiny2Writer; | ||||
| import net.fabricmc.mappingio.tree.MappingTree; | ||||
| import net.fabricmc.mappingio.tree.MemoryMappingTree; | ||||
| import net.fabricmc.stitch.Command; | ||||
| import net.fabricmc.stitch.commands.CommandProposeFieldNames; | ||||
| 
 | ||||
| public class MappingsProviderImpl implements MappingsProvider { | ||||
| 	public String mappingsIdentifier; | ||||
| public class MappingsProviderImpl implements MappingsProvider, SharedService { | ||||
| 	private static final Logger LOGGER = LoggerFactory.getLogger(MappingsProviderImpl.class); | ||||
| 
 | ||||
| 	private Path mappingsWorkingDir; | ||||
| 	private Path intermediaryTiny; | ||||
| 	private boolean hasRefreshed = false; | ||||
| 	private Supplier<MemoryMappingTree> mappingTree; | ||||
| 	public final String mappingsIdentifier; | ||||
| 
 | ||||
| 	private final Path mappingsWorkingDir; | ||||
| 	// The mappings that gradle gives us | ||||
| 	private Path baseTinyMappings; | ||||
| 	private final Path baseTinyMappings; | ||||
| 	// The mappings we use in practice | ||||
| 	public Path tinyMappings; | ||||
| 	public Path tinyMappingsJar; | ||||
| 	private Path unpickDefinitions; | ||||
| 	public final Path tinyMappings; | ||||
| 	public final Path tinyMappingsJar; | ||||
| 	private final Path unpickDefinitions; | ||||
| 
 | ||||
| 	private boolean hasUnpickDefinitions; | ||||
| 	private UnpickMetadata unpickMetadata; | ||||
| 	private MemoryMappingTree mappingTree; | ||||
| 	private Map<String, String> signatureFixes; | ||||
| 
 | ||||
| 	private final Project project; | ||||
| 	private final MinecraftProvider minecraftProvider; | ||||
| 	private final LoomGradleExtension extension; | ||||
| 	public MappingsProviderImpl(Project project, MinecraftProvider minecraftProvider) { | ||||
| 		this.project = project; | ||||
| 		this.minecraftProvider = minecraftProvider; | ||||
| 		this.extension = LoomGradleExtension.get(project); | ||||
| 	private final Supplier<IntermediaryService> intermediaryService; | ||||
| 
 | ||||
| 	private MappingsProviderImpl(String mappingsIdentifier, Path mappingsWorkingDir, Supplier<IntermediaryService> intermediaryService) { | ||||
| 		this.mappingsIdentifier = mappingsIdentifier; | ||||
| 
 | ||||
| 		this.mappingsWorkingDir = mappingsWorkingDir; | ||||
| 		this.baseTinyMappings = mappingsWorkingDir.resolve("mappings-base.tiny"); | ||||
| 		this.tinyMappings = mappingsWorkingDir.resolve("mappings.tiny"); | ||||
| 		this.tinyMappingsJar = mappingsWorkingDir.resolve("mappings.jar"); | ||||
| 		this.unpickDefinitions = mappingsWorkingDir.resolve("mappings.unpick"); | ||||
| 
 | ||||
| 		this.intermediaryService = intermediaryService; | ||||
| 	} | ||||
| 
 | ||||
| 	public static synchronized MappingsProviderImpl getInstance(Project project, DependencyInfo dependency, MinecraftProvider minecraftProvider) { | ||||
| 		return SharedServiceManager.get(project).getOrCreateService("MappingsProvider:%s:%s".formatted(dependency.getDepString(), minecraftProvider.minecraftVersion()), () -> { | ||||
| 			Supplier<IntermediaryService> intermediaryService = Suppliers.memoize(() -> IntermediaryService.getInstance(project, minecraftProvider)); | ||||
| 			return create(dependency, minecraftProvider, intermediaryService); | ||||
| 		}); | ||||
| 	} | ||||
| 
 | ||||
| 	public MemoryMappingTree getMappings() throws IOException { | ||||
| 		return Objects.requireNonNull(mappingTree, "Cannot get mappings before they have been read"); | ||||
| 		return Objects.requireNonNull(mappingTree, "Cannot get mappings before they have been read").get(); | ||||
| 	} | ||||
| 
 | ||||
| 	public void provide() throws Exception { | ||||
| 		final DependencyInfo dependency = DependencyInfo.create(project, Constants.Configurations.MAPPINGS); | ||||
| 	private static MappingsProviderImpl create(DependencyInfo dependency, MinecraftProvider minecraftProvider, Supplier<IntermediaryService> intermediaryService) { | ||||
| 		final String version = dependency.getResolvedVersion(); | ||||
| 		final Path inputJar = dependency.resolveFile().orElseThrow(() -> new RuntimeException("Could not resolve mappings: " + dependency)).toPath(); | ||||
| 		final String mappingsName = StringUtils.removeSuffix(dependency.getDependency().getGroup() + "." + dependency.getDependency().getName(), "-unmerged"); | ||||
| 
 | ||||
| 		project.getLogger().info(":setting up mappings (" + dependency.getDependency().getName() + " " + dependency.getResolvedVersion() + ")"); | ||||
| 		final TinyJarInfo jarInfo = TinyJarInfo.get(inputJar); | ||||
| 		jarInfo.minecraftVersionId().ifPresent(id -> { | ||||
| 			if (!minecraftProvider.minecraftVersion().equals(id)) { | ||||
| 				LOGGER.warn("The mappings (%s) were not build for minecraft version (%s) produce with caution.".formatted(dependency.getDepString(), minecraftProvider.minecraftVersion())); | ||||
| 			} | ||||
| 		}); | ||||
| 
 | ||||
| 		String version = dependency.getResolvedVersion(); | ||||
| 		File mappingsJar = dependency.resolveFile().orElseThrow(() -> new RuntimeException("Could not find yarn mappings: " + dependency)); | ||||
| 		final String mappingsIdentifier = createMappingsIdentifier(mappingsName, version, getMappingsClassifier(dependency, jarInfo.v2()), minecraftProvider.minecraftVersion()); | ||||
| 		final Path workingDir = minecraftProvider.dir(mappingsIdentifier).toPath(); | ||||
| 
 | ||||
| 		String mappingsName = StringUtils.removeSuffix(dependency.getDependency().getGroup() + "." + dependency.getDependency().getName(), "-unmerged"); | ||||
| 		boolean isV2 = isV2(dependency, mappingsJar); | ||||
| 		this.mappingsIdentifier = createMappingsIdentifier(mappingsName, version, getMappingsClassifier(dependency, isV2)); | ||||
| 		var mappingProvider = new MappingsProviderImpl(mappingsIdentifier, workingDir, intermediaryService); | ||||
| 
 | ||||
| 		initFiles(); | ||||
| 		try { | ||||
| 			mappingProvider.setup(minecraftProvider, inputJar); | ||||
| 		} catch (IOException e) { | ||||
| 			cleanWorkingDirectory(workingDir); | ||||
| 			throw new UncheckedIOException("Failed to setup mappings: " + dependency.getDepString(), e); | ||||
| 		} | ||||
| 
 | ||||
| 		return mappingProvider; | ||||
| 	} | ||||
| 
 | ||||
| 	private void setup(MinecraftProvider minecraftProvider, Path inputJar) throws IOException { | ||||
| 		if (isRefreshDeps()) { | ||||
| 			cleanWorkingDirectory(mappingsWorkingDir); | ||||
| 		} | ||||
| 
 | ||||
| 		if (Files.notExists(tinyMappings) || isRefreshDeps()) { | ||||
| 			storeMappings(project, minecraftProvider, mappingsJar.toPath()); | ||||
| 			storeMappings(minecraftProvider, inputJar); | ||||
| 		} else { | ||||
| 			try (FileSystem fileSystem = FileSystems.newFileSystem(mappingsJar.toPath(), (ClassLoader) null)) { | ||||
| 			try (FileSystem fileSystem = FileSystems.newFileSystem(inputJar, (ClassLoader) null)) { | ||||
| 				extractExtras(fileSystem); | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		mappingTree = readMappings(); | ||||
| 
 | ||||
| 		if (Files.notExists(tinyMappingsJar) || isRefreshDeps()) { | ||||
| 			Files.deleteIfExists(tinyMappingsJar); | ||||
| 			ZipUtils.add(tinyMappingsJar, "mappings/mappings.tiny", Files.readAllBytes(tinyMappings)); | ||||
| 		} | ||||
| 
 | ||||
| 		mappingTree = Suppliers.memoize(this::readMappings); | ||||
| 	} | ||||
| 
 | ||||
| 	public void applyToProject(Project project, DependencyInfo dependency) { | ||||
| 		if (hasUnpickDefinitions()) { | ||||
| 			String notation = String.format("%s:%s:%s:constants", | ||||
| 					dependency.getDependency().getGroup(), | ||||
|  | @ -137,13 +165,13 @@ public class MappingsProviderImpl implements MappingsProvider { | |||
| 			); | ||||
| 
 | ||||
| 			project.getDependencies().add(Constants.Configurations.MAPPING_CONSTANTS, notation); | ||||
| 			populateUnpickClasspath(); | ||||
| 			populateUnpickClasspath(project); | ||||
| 		} | ||||
| 
 | ||||
| 		project.getDependencies().add(Constants.Configurations.MAPPINGS_FINAL, project.files(tinyMappingsJar.toFile())); | ||||
| 	} | ||||
| 
 | ||||
| 	private String getMappingsClassifier(DependencyInfo dependency, boolean isV2) { | ||||
| 	private static String getMappingsClassifier(DependencyInfo dependency, boolean isV2) { | ||||
| 		String[] depStringSplit = dependency.getDepString().split(":"); | ||||
| 
 | ||||
| 		if (depStringSplit.length >= 4) { | ||||
|  | @ -153,42 +181,22 @@ public class MappingsProviderImpl implements MappingsProvider { | |||
| 		return isV2 ? "-v2" : ""; | ||||
| 	} | ||||
| 
 | ||||
| 	private boolean isV2(DependencyInfo dependency, File mappingsJar) throws IOException { | ||||
| 		String minecraftVersion = minecraftProvider.minecraftVersion(); | ||||
| 	private void storeMappings(MinecraftProvider minecraftProvider, Path inputJar) throws IOException { | ||||
| 		LOGGER.info(":extracting " + inputJar.getFileName()); | ||||
| 
 | ||||
| 		// Only do this for official yarn, there isn't really a way we can get the mc version for all mappings | ||||
| 		if (dependency.getDependency().getGroup() != null && dependency.getDependency().getGroup().equals("net.fabricmc") && dependency.getDependency().getName().equals("yarn") && dependency.getDependency().getVersion() != null) { | ||||
| 			String yarnVersion = dependency.getDependency().getVersion(); | ||||
| 			char separator = yarnVersion.contains("+build.") ? '+' : yarnVersion.contains("-") ? '-' : '.'; | ||||
| 			String yarnMinecraftVersion = yarnVersion.substring(0, yarnVersion.lastIndexOf(separator)); | ||||
| 
 | ||||
| 			if (!yarnMinecraftVersion.equalsIgnoreCase(minecraftVersion)) { | ||||
| 				project.getLogger().warn("Minecraft Version ({}) does not match yarn's minecraft version ({})", minecraftVersion, yarnMinecraftVersion); | ||||
| 			} | ||||
| 
 | ||||
| 			// We can save reading the zip file + header by checking the file name | ||||
| 			return mappingsJar.getName().endsWith("-v2.jar"); | ||||
| 		} else { | ||||
| 			return doesJarContainV2Mappings(mappingsJar.toPath()); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	private void storeMappings(Project project, MinecraftProvider minecraftProvider, Path yarnJar) throws IOException { | ||||
| 		project.getLogger().info(":extracting " + yarnJar.getFileName()); | ||||
| 
 | ||||
| 		try (FileSystem fileSystem = FileSystems.newFileSystem(yarnJar, (ClassLoader) null)) { | ||||
| 		try (FileSystem fileSystem = FileSystems.newFileSystem(inputJar, (ClassLoader) null)) { | ||||
| 			extractMappings(fileSystem, baseTinyMappings); | ||||
| 			extractExtras(fileSystem); | ||||
| 		} | ||||
| 
 | ||||
| 		if (areMappingsV2(baseTinyMappings)) { | ||||
| 			// These are unmerged v2 mappings | ||||
| 			mergeAndSaveMappings(project, baseTinyMappings, tinyMappings); | ||||
| 			MappingsMerger.mergeAndSaveMappings(baseTinyMappings, tinyMappings, intermediaryService.get()); | ||||
| 		} else { | ||||
| 			if (minecraftProvider instanceof MergedMinecraftProvider mergedMinecraftProvider) { | ||||
| 				// These are merged v1 mappings | ||||
| 				Files.deleteIfExists(tinyMappings); | ||||
| 				project.getLogger().lifecycle(":populating field names"); | ||||
| 				LOGGER.info(":populating field names"); | ||||
| 				suggestFieldNames(mergedMinecraftProvider, baseTinyMappings, tinyMappings); | ||||
| 			} else { | ||||
| 				throw new UnsupportedOperationException("V1 mappings only support merged minecraft"); | ||||
|  | @ -196,10 +204,14 @@ public class MappingsProviderImpl implements MappingsProvider { | |||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	private MemoryMappingTree readMappings() throws IOException { | ||||
| 		MemoryMappingTree mappingTree = new MemoryMappingTree(); | ||||
| 		MappingReader.read(tinyMappings, mappingTree); | ||||
| 		return mappingTree; | ||||
| 	private MemoryMappingTree readMappings() { | ||||
| 		try { | ||||
| 			MemoryMappingTree mappingTree = new MemoryMappingTree(); | ||||
| 			MappingReader.read(tinyMappings, mappingTree); | ||||
| 			return mappingTree; | ||||
| 		} catch (IOException e) { | ||||
| 			throw new UncheckedIOException("Failed to read mappings", e); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	private static boolean areMappingsV2(Path path) throws IOException { | ||||
|  | @ -208,15 +220,7 @@ public class MappingsProviderImpl implements MappingsProvider { | |||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	private static boolean doesJarContainV2Mappings(Path path) throws IOException { | ||||
| 		try (FileSystem fs = FileSystems.newFileSystem(path, (ClassLoader) null)) { | ||||
| 			try (BufferedReader reader = Files.newBufferedReader(fs.getPath("mappings", "mappings.tiny"))) { | ||||
| 				return MappingReader.detectFormat(reader) == MappingFormat.TINY_2; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	private static void extractMappings(Path jar, Path extractTo) throws IOException { | ||||
| 	public static void extractMappings(Path jar, Path extractTo) throws IOException { | ||||
| 		try (FileSystem unmergedIntermediaryFs = FileSystems.newFileSystem(jar, (ClassLoader) null)) { | ||||
| 			extractMappings(unmergedIntermediaryFs, extractTo); | ||||
| 		} | ||||
|  | @ -271,7 +275,7 @@ public class MappingsProviderImpl implements MappingsProvider { | |||
| 		); | ||||
| 	} | ||||
| 
 | ||||
| 	private void populateUnpickClasspath() { | ||||
| 	private void populateUnpickClasspath(Project project) { | ||||
| 		String unpickCliName = "unpick-cli"; | ||||
| 		project.getDependencies().add(Constants.Configurations.UNPICK_CLASSPATH, | ||||
| 				String.format("%s:%s:%s", unpickMetadata.unpickGroup, unpickCliName, unpickMetadata.unpickVersion) | ||||
|  | @ -292,76 +296,6 @@ public class MappingsProviderImpl implements MappingsProvider { | |||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	private void mergeAndSaveMappings(Project project, Path from, Path out) throws IOException { | ||||
| 		Stopwatch stopwatch = Stopwatch.createStarted(); | ||||
| 		project.getLogger().info(":merging mappings"); | ||||
| 
 | ||||
| 		MemoryMappingTree intermediaryTree = new MemoryMappingTree(); | ||||
| 		readIntermediaryTree().accept(new MappingSourceNsSwitch(intermediaryTree, MappingsNamespace.INTERMEDIARY.toString())); | ||||
| 
 | ||||
| 		try (BufferedReader reader = Files.newBufferedReader(from, StandardCharsets.UTF_8)) { | ||||
| 			Tiny2Reader.read(reader, intermediaryTree); | ||||
| 		} | ||||
| 
 | ||||
| 		MemoryMappingTree officialTree = new MemoryMappingTree(); | ||||
| 		MappingNsCompleter nsCompleter = new MappingNsCompleter(officialTree, Map.of(MappingsNamespace.OFFICIAL.toString(), MappingsNamespace.INTERMEDIARY.toString())); | ||||
| 		MappingSourceNsSwitch nsSwitch = new MappingSourceNsSwitch(nsCompleter, MappingsNamespace.OFFICIAL.toString()); | ||||
| 		intermediaryTree.accept(nsSwitch); | ||||
| 
 | ||||
| 		inheritMappedNamesOfEnclosingClasses(officialTree); | ||||
| 
 | ||||
| 		try (Tiny2Writer writer = new Tiny2Writer(Files.newBufferedWriter(out, StandardCharsets.UTF_8), false)) { | ||||
| 			officialTree.accept(writer); | ||||
| 		} | ||||
| 
 | ||||
| 		project.getLogger().info(":merged mappings in " + stopwatch.stop()); | ||||
| 	} | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Searches the mapping tree for inner classes with no mapped name, whose enclosing classes have mapped names. | ||||
| 	 * Currently, Yarn does not export mappings for these inner classes. | ||||
| 	 */ | ||||
| 	private void inheritMappedNamesOfEnclosingClasses(MemoryMappingTree tree) { | ||||
| 		int intermediaryIdx = tree.getNamespaceId("intermediary"); | ||||
| 		int namedIdx = tree.getNamespaceId("named"); | ||||
| 
 | ||||
| 		// The tree does not have an index by intermediary names by default | ||||
| 		tree.setIndexByDstNames(true); | ||||
| 
 | ||||
| 		for (MappingTree.ClassMapping classEntry : tree.getClasses()) { | ||||
| 			String intermediaryName = classEntry.getDstName(intermediaryIdx); | ||||
| 			String namedName = classEntry.getDstName(namedIdx); | ||||
| 
 | ||||
| 			if (intermediaryName.equals(namedName) && intermediaryName.contains("$")) { | ||||
| 				String[] path = intermediaryName.split(Pattern.quote("$")); | ||||
| 				int parts = path.length; | ||||
| 
 | ||||
| 				for (int i = parts - 2; i >= 0; i--) { | ||||
| 					String currentPath = String.join("$", Arrays.copyOfRange(path, 0, i + 1)); | ||||
| 					String namedParentClass = tree.mapClassName(currentPath, intermediaryIdx, namedIdx); | ||||
| 
 | ||||
| 					if (!namedParentClass.equals(currentPath)) { | ||||
| 						classEntry.setDstName(namedParentClass | ||||
| 										+ "$" + String.join("$", Arrays.copyOfRange(path, i + 1, path.length)), | ||||
| 								namedIdx); | ||||
| 						break; | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	private MemoryMappingTree readIntermediaryTree() throws IOException { | ||||
| 		MemoryMappingTree tree = new MemoryMappingTree(); | ||||
| 		MappingNsCompleter nsCompleter = new MappingNsCompleter(tree, Collections.singletonMap(MappingsNamespace.NAMED.toString(), MappingsNamespace.INTERMEDIARY.toString()), true); | ||||
| 
 | ||||
| 		try (BufferedReader reader = Files.newBufferedReader(getIntermediaryTiny(), StandardCharsets.UTF_8)) { | ||||
| 			Tiny2Reader.read(reader, nsCompleter); | ||||
| 		} | ||||
| 
 | ||||
| 		return tree; | ||||
| 	} | ||||
| 
 | ||||
| 	private void suggestFieldNames(MergedMinecraftProvider minecraftProvider, Path oldMappings, Path newMappings) { | ||||
| 		Command command = new CommandProposeFieldNames(); | ||||
| 		runCommand(command, minecraftProvider.getMergedJar().getAbsolutePath(), | ||||
|  | @ -377,19 +311,7 @@ public class MappingsProviderImpl implements MappingsProvider { | |||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	private void initFiles() { | ||||
| 		mappingsWorkingDir = minecraftProvider.dir(mappingsIdentifier).toPath(); | ||||
| 		baseTinyMappings = mappingsWorkingDir.resolve("mappings-base.tiny"); | ||||
| 		tinyMappings = mappingsWorkingDir.resolve("mappings.tiny"); | ||||
| 		tinyMappingsJar = mappingsWorkingDir.resolve("mappings.jar"); | ||||
| 		unpickDefinitions = mappingsWorkingDir.resolve("mappings.unpick"); | ||||
| 
 | ||||
| 		if (isRefreshDeps()) { | ||||
| 			cleanFiles(); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	public void cleanFiles() { | ||||
| 	private static void cleanWorkingDirectory(Path mappingsWorkingDir) { | ||||
| 		try { | ||||
| 			if (Files.exists(mappingsWorkingDir)) { | ||||
| 				Files.walkFileTree(mappingsWorkingDir, new DeletingFileVisitor()); | ||||
|  | @ -401,34 +323,20 @@ public class MappingsProviderImpl implements MappingsProvider { | |||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	public Path getIntermediaryTiny() throws IOException { | ||||
| 		if (intermediaryTiny == null) { | ||||
| 			intermediaryTiny = minecraftProvider.file("intermediary-v2.tiny").toPath(); | ||||
| 
 | ||||
| 			if (!Files.exists(intermediaryTiny) || (isRefreshDeps() && !hasRefreshed)) { | ||||
| 				hasRefreshed = true; | ||||
| 
 | ||||
| 				// Download and extract intermediary | ||||
| 				String encodedMinecraftVersion = UrlEscapers.urlFragmentEscaper().escape(minecraftProvider.minecraftVersion()); | ||||
| 				String intermediaryArtifactUrl = extension.getIntermediaryUrl(encodedMinecraftVersion); | ||||
| 				File intermediaryJar = minecraftProvider.file("intermediary-v2.jar"); | ||||
| 				DownloadUtil.downloadIfChanged(new URL(intermediaryArtifactUrl), intermediaryJar, project.getLogger()); | ||||
| 				extractMappings(intermediaryJar.toPath(), intermediaryTiny); | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		return intermediaryTiny; | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public Path mappingsWorkingDir() { | ||||
| 		return mappingsWorkingDir; | ||||
| 	} | ||||
| 
 | ||||
| 	private String createMappingsIdentifier(String mappingsName, String version, String classifier) { | ||||
| 	@Override | ||||
| 	public File intermediaryTinyFile() { | ||||
| 		return intermediaryService.get().getIntermediaryTiny().toFile(); | ||||
| 	} | ||||
| 
 | ||||
| 	private static String createMappingsIdentifier(String mappingsName, String version, String classifier, String minecraftVersion) { | ||||
| 		//          mappingsName      . mcVersion . version        classifier | ||||
| 		// Example: net.fabricmc.yarn . 1_16_5    . 1.16.5+build.5 -v2 | ||||
| 		return mappingsName + "." + minecraftProvider.minecraftVersion().replace(' ', '_').replace('.', '_').replace('-', '_') + "." + version + classifier; | ||||
| 		return mappingsName + "." + minecraftVersion.replace(' ', '_').replace('.', '_').replace('-', '_') + "." + version + classifier; | ||||
| 	} | ||||
| 
 | ||||
| 	public String mappingsIdentifier() { | ||||
|  | @ -448,15 +356,6 @@ public class MappingsProviderImpl implements MappingsProvider { | |||
| 		return signatureFixes; | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public File intermediaryTinyFile() { | ||||
| 		try { | ||||
| 			return getIntermediaryTiny().toFile(); | ||||
| 		} catch (IOException e) { | ||||
| 			throw new RuntimeException("Failed to get intermediary", e); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	public String getBuildServiceName(String name, String from, String to) { | ||||
| 		return "%s:%s:%s>%S".formatted(name, mappingsIdentifier(), from, to); | ||||
| 	} | ||||
|  | @ -464,7 +363,12 @@ public class MappingsProviderImpl implements MappingsProvider { | |||
| 	public record UnpickMetadata(String unpickGroup, String unpickVersion) { | ||||
| 	} | ||||
| 
 | ||||
| 	protected boolean isRefreshDeps() { | ||||
| 	protected static boolean isRefreshDeps() { | ||||
| 		return LoomGradlePlugin.refreshDeps; | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public void close() throws IOException { | ||||
| 		mappingTree = null; | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -24,20 +24,17 @@ | |||
| 
 | ||||
| package net.fabricmc.loom.configuration.providers.mappings.intermediary; | ||||
| 
 | ||||
| import java.io.BufferedReader; | ||||
| import java.io.File; | ||||
| import java.io.IOException; | ||||
| import java.nio.charset.StandardCharsets; | ||||
| import java.nio.file.Files; | ||||
| import java.util.Collections; | ||||
| import java.util.function.Supplier; | ||||
| 
 | ||||
| import net.fabricmc.loom.api.mappings.layered.MappingLayer; | ||||
| import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; | ||||
| import net.fabricmc.mappingio.MappingVisitor; | ||||
| import net.fabricmc.mappingio.adapter.MappingNsCompleter; | ||||
| import net.fabricmc.mappingio.format.Tiny2Reader; | ||||
| import net.fabricmc.mappingio.tree.MemoryMappingTree; | ||||
| 
 | ||||
| public record IntermediaryMappingLayer(File tinyFile) implements MappingLayer { | ||||
| public record IntermediaryMappingLayer(Supplier<MemoryMappingTree> memoryMappingTree) implements MappingLayer { | ||||
| 	@Override | ||||
| 	public MappingsNamespace getSourceNamespace() { | ||||
| 		return MappingsNamespace.OFFICIAL; | ||||
|  | @ -48,8 +45,6 @@ public record IntermediaryMappingLayer(File tinyFile) implements MappingLayer { | |||
| 		// Populate named with intermediary and add Add a "named" namespace | ||||
| 		MappingNsCompleter nsCompleter = new MappingNsCompleter(mappingVisitor, Collections.singletonMap(MappingsNamespace.NAMED.toString(), MappingsNamespace.INTERMEDIARY.toString()), true); | ||||
| 
 | ||||
| 		try (BufferedReader reader = Files.newBufferedReader(tinyFile().toPath(), StandardCharsets.UTF_8)) { | ||||
| 			Tiny2Reader.read(reader, nsCompleter); | ||||
| 		} | ||||
| 		memoryMappingTree.get().accept(nsCompleter); | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -30,6 +30,6 @@ import net.fabricmc.loom.api.mappings.layered.spec.MappingsSpec; | |||
| public record IntermediaryMappingsSpec() implements MappingsSpec<IntermediaryMappingLayer> { | ||||
| 	@Override | ||||
| 	public IntermediaryMappingLayer createLayer(MappingContext context) { | ||||
| 		return new IntermediaryMappingLayer(context.mappingsProvider().intermediaryTinyFile()); | ||||
| 		return new IntermediaryMappingLayer(context.intermediaryTree()); | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -0,0 +1,110 @@ | |||
| /* | ||||
|  * 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.mappings.tiny; | ||||
| 
 | ||||
| import java.io.BufferedReader; | ||||
| import java.io.IOException; | ||||
| import java.nio.charset.StandardCharsets; | ||||
| import java.nio.file.Files; | ||||
| import java.nio.file.Path; | ||||
| import java.util.Arrays; | ||||
| import java.util.Map; | ||||
| import java.util.regex.Pattern; | ||||
| 
 | ||||
| import com.google.common.base.Stopwatch; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
| 
 | ||||
| import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; | ||||
| import net.fabricmc.loom.configuration.providers.mappings.IntermediaryService; | ||||
| import net.fabricmc.mappingio.adapter.MappingNsCompleter; | ||||
| import net.fabricmc.mappingio.adapter.MappingSourceNsSwitch; | ||||
| import net.fabricmc.mappingio.format.Tiny2Reader; | ||||
| import net.fabricmc.mappingio.format.Tiny2Writer; | ||||
| import net.fabricmc.mappingio.tree.MappingTree; | ||||
| import net.fabricmc.mappingio.tree.MemoryMappingTree; | ||||
| 
 | ||||
| public final class MappingsMerger { | ||||
| 	private static final Logger LOGGER = LoggerFactory.getLogger(MappingsMerger.class); | ||||
| 
 | ||||
| 	public static void mergeAndSaveMappings(Path from, Path out, IntermediaryService intermediaryService) throws IOException { | ||||
| 		Stopwatch stopwatch = Stopwatch.createStarted(); | ||||
| 		LOGGER.info(":merging mappings"); | ||||
| 
 | ||||
| 		MemoryMappingTree intermediaryTree = new MemoryMappingTree(); | ||||
| 		intermediaryService.getMemoryMappingTree().accept(new MappingSourceNsSwitch(intermediaryTree, MappingsNamespace.INTERMEDIARY.toString())); | ||||
| 
 | ||||
| 		try (BufferedReader reader = Files.newBufferedReader(from, StandardCharsets.UTF_8)) { | ||||
| 			Tiny2Reader.read(reader, intermediaryTree); | ||||
| 		} | ||||
| 
 | ||||
| 		MemoryMappingTree officialTree = new MemoryMappingTree(); | ||||
| 		MappingNsCompleter nsCompleter = new MappingNsCompleter(officialTree, Map.of(MappingsNamespace.OFFICIAL.toString(), MappingsNamespace.INTERMEDIARY.toString())); | ||||
| 		MappingSourceNsSwitch nsSwitch = new MappingSourceNsSwitch(nsCompleter, MappingsNamespace.OFFICIAL.toString()); | ||||
| 		intermediaryTree.accept(nsSwitch); | ||||
| 
 | ||||
| 		inheritMappedNamesOfEnclosingClasses(officialTree); | ||||
| 
 | ||||
| 		try (Tiny2Writer writer = new Tiny2Writer(Files.newBufferedWriter(out, StandardCharsets.UTF_8), false)) { | ||||
| 			officialTree.accept(writer); | ||||
| 		} | ||||
| 
 | ||||
| 		LOGGER.info(":merged mappings in " + stopwatch.stop()); | ||||
| 	} | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Searches the mapping tree for inner classes with no mapped name, whose enclosing classes have mapped names. | ||||
| 	 * Currently, Yarn does not export mappings for these inner classes. | ||||
| 	 */ | ||||
| 	private static void inheritMappedNamesOfEnclosingClasses(MemoryMappingTree tree) { | ||||
| 		int intermediaryIdx = tree.getNamespaceId("intermediary"); | ||||
| 		int namedIdx = tree.getNamespaceId("named"); | ||||
| 
 | ||||
| 		// The tree does not have an index by intermediary names by default | ||||
| 		tree.setIndexByDstNames(true); | ||||
| 
 | ||||
| 		for (MappingTree.ClassMapping classEntry : tree.getClasses()) { | ||||
| 			String intermediaryName = classEntry.getDstName(intermediaryIdx); | ||||
| 			String namedName = classEntry.getDstName(namedIdx); | ||||
| 
 | ||||
| 			if (intermediaryName.equals(namedName) && intermediaryName.contains("$")) { | ||||
| 				String[] path = intermediaryName.split(Pattern.quote("$")); | ||||
| 				int parts = path.length; | ||||
| 
 | ||||
| 				for (int i = parts - 2; i >= 0; i--) { | ||||
| 					String currentPath = String.join("$", Arrays.copyOfRange(path, 0, i + 1)); | ||||
| 					String namedParentClass = tree.mapClassName(currentPath, intermediaryIdx, namedIdx); | ||||
| 
 | ||||
| 					if (!namedParentClass.equals(currentPath)) { | ||||
| 						classEntry.setDstName(namedParentClass | ||||
| 										+ "$" + String.join("$", Arrays.copyOfRange(path, i + 1, path.length)), | ||||
| 								namedIdx); | ||||
| 						break; | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | @ -0,0 +1,55 @@ | |||
| /* | ||||
|  * 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.mappings.tiny; | ||||
| 
 | ||||
| import java.io.BufferedReader; | ||||
| import java.io.IOException; | ||||
| import java.io.UncheckedIOException; | ||||
| import java.nio.file.FileSystem; | ||||
| import java.nio.file.FileSystems; | ||||
| import java.nio.file.Files; | ||||
| import java.nio.file.Path; | ||||
| import java.util.Optional; | ||||
| 
 | ||||
| import net.fabricmc.mappingio.MappingReader; | ||||
| import net.fabricmc.mappingio.format.MappingFormat; | ||||
| 
 | ||||
| public record TinyJarInfo(boolean v2, Optional<String> minecraftVersionId) { | ||||
| 	public static TinyJarInfo get(Path jar) { | ||||
| 		try { | ||||
| 			return new TinyJarInfo(doesJarContainV2Mappings(jar), Optional.empty()); | ||||
| 		} catch (IOException e) { | ||||
| 			throw new UncheckedIOException("Failed to read tiny jar info", e); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	private static boolean doesJarContainV2Mappings(Path path) throws IOException { | ||||
| 		try (FileSystem fs = FileSystems.newFileSystem(path, (ClassLoader) null)) { | ||||
| 			try (BufferedReader reader = Files.newBufferedReader(fs.getPath("mappings", "mappings.tiny"))) { | ||||
| 				return MappingReader.detectFormat(reader) == MappingFormat.TINY_2; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | @ -41,6 +41,6 @@ public final class DecompilerConfiguration { | |||
| 	} | ||||
| 
 | ||||
| 	private static void registerDecompiler(Project project, String name, Class<? extends LoomDecompiler> decompilerClass) { | ||||
| 		LoomGradleExtension.get(project).getDecompilerOptions().register(name, options -> options.getDecompilerClassname().set(decompilerClass.getName())); | ||||
| 		LoomGradleExtension.get(project).getDecompilerOptions().register(name, options -> options.getDecompilerClassName().set(decompilerClass.getName())); | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -24,7 +24,6 @@ | |||
| 
 | ||||
| package net.fabricmc.loom.extension; | ||||
| 
 | ||||
| import java.io.File; | ||||
| import java.nio.file.Path; | ||||
| import java.util.ArrayList; | ||||
| import java.util.HashMap; | ||||
|  | @ -41,7 +40,6 @@ import org.gradle.api.Project; | |||
| import org.gradle.api.artifacts.Configuration; | ||||
| import org.gradle.api.file.ConfigurableFileCollection; | ||||
| import org.gradle.api.file.FileCollection; | ||||
| import org.gradle.api.tasks.SourceSet; | ||||
| 
 | ||||
| import net.fabricmc.loom.LoomGradleExtension; | ||||
| import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; | ||||
|  | @ -60,7 +58,6 @@ public class LoomGradleExtensionImpl extends LoomGradleExtensionApiImpl implemen | |||
| 	private final LoomFiles loomFiles; | ||||
| 	private final ConfigurableFileCollection unmappedMods; | ||||
| 
 | ||||
| 	private final ConfigurableFileCollection mixinMappings; | ||||
| 	private final MappingSet[] srcMappingCache = new MappingSet[2]; | ||||
| 	private final Mercury[] srcMercuryCache = new Mercury[2]; | ||||
| 	private final Map<String, NamedDomainObjectProvider<Configuration>> lazyConfigurations = new HashMap<>(); | ||||
|  | @ -79,7 +76,6 @@ public class LoomGradleExtensionImpl extends LoomGradleExtensionApiImpl implemen | |||
| 		this.project = project; | ||||
| 		// Initiate with newInstance to allow gradle to decorate our extension | ||||
| 		this.mixinApExtension = project.getObjects().newInstance(MixinExtensionImpl.class, project); | ||||
| 		this.mixinMappings = project.getObjects().fileCollection(); | ||||
| 		this.loomFiles = files; | ||||
| 		this.unmappedMods = project.files(); | ||||
| 	} | ||||
|  | @ -94,18 +90,6 @@ public class LoomGradleExtensionImpl extends LoomGradleExtensionApiImpl implemen | |||
| 		return loomFiles; | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public synchronized File getMixinMappings(SourceSet sourceSet) { | ||||
| 		File mixinMapping = new File(getFiles().getProjectBuildCache(), "mixin-map-" + getMappingsProvider().mappingsIdentifier() + "." + sourceSet.getName() + ".tiny"); | ||||
| 		mixinMappings.from(getProject().files(mixinMapping)); | ||||
| 		return mixinMapping; | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public FileCollection getAllMixinMappings() { | ||||
| 		return mixinMappings.filter(File::exists); | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public void setDependencyManager(LoomDependencyManager dependencyManager) { | ||||
| 		this.dependencyManager = dependencyManager; | ||||
|  |  | |||
							
								
								
									
										108
									
								
								src/main/java/net/fabricmc/loom/task/PrepareJarRemapTask.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								src/main/java/net/fabricmc/loom/task/PrepareJarRemapTask.java
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,108 @@ | |||
| /* | ||||
|  * 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.task; | ||||
| 
 | ||||
| import java.nio.file.Path; | ||||
| 
 | ||||
| import javax.inject.Inject; | ||||
| 
 | ||||
| import org.gradle.api.file.RegularFileProperty; | ||||
| import org.gradle.api.provider.Property; | ||||
| import org.gradle.api.tasks.InputFile; | ||||
| import org.gradle.api.tasks.TaskAction; | ||||
| import org.gradle.workers.WorkAction; | ||||
| import org.gradle.workers.WorkParameters; | ||||
| import org.gradle.workers.WorkQueue; | ||||
| import org.gradle.workers.WorkerExecutor; | ||||
| 
 | ||||
| import net.fabricmc.loom.task.service.TinyRemapperService; | ||||
| import net.fabricmc.loom.util.service.UnsafeWorkQueueHelper; | ||||
| import net.fabricmc.tinyremapper.TinyRemapper; | ||||
| 
 | ||||
| /** | ||||
|  * The prepare remap task runs before all other jar remap tasks, should be used to setup tiny remapper. | ||||
|  */ | ||||
| public abstract class PrepareJarRemapTask extends AbstractLoomTask { | ||||
| 	private final RemapJarTask remapJarTask; | ||||
| 	@InputFile | ||||
| 	public abstract RegularFileProperty getInputFile(); | ||||
| 
 | ||||
| 	@Inject | ||||
| 	public PrepareJarRemapTask(RemapJarTask remapJarTask) { | ||||
| 		this.remapJarTask = remapJarTask; | ||||
| 
 | ||||
| 		getInputFile().set(remapJarTask.getInputFile()); | ||||
| 		// TODO can this be up-to-date when the main task is up-to date? | ||||
| 		getOutputs().upToDateWhen((o) -> false); | ||||
| 
 | ||||
| 		getProject().getGradle().allprojects(project -> { | ||||
| 			project.getTasks().configureEach(task -> { | ||||
| 				if (task instanceof PrepareJarRemapTask otherTask) { | ||||
| 					if (otherTask == this) return; | ||||
| 
 | ||||
| 					// Ensure that all other prepare tasks inputs have completed | ||||
| 					dependsOn(otherTask.getInputs()); | ||||
| 					mustRunAfter(otherTask.getInputs()); | ||||
| 				} | ||||
| 			}); | ||||
| 		}); | ||||
| 	} | ||||
| 
 | ||||
| 	@Inject | ||||
| 	protected abstract WorkerExecutor getWorkerExecutor(); | ||||
| 
 | ||||
| 	@TaskAction | ||||
| 	public void run() { | ||||
| 		final WorkQueue workQueue = getWorkerExecutor().noIsolation(); | ||||
| 
 | ||||
| 		workQueue.submit(ReadInputsAction.class, params -> { | ||||
| 			params.getTinyRemapperBuildServiceUuid().set(UnsafeWorkQueueHelper.create(getProject(), remapJarTask.getTinyRemapperService())); | ||||
| 			params.getInputTagName().set(remapJarTask.getInputTagName()); | ||||
| 			params.getInputFile().set(getInputFile()); | ||||
| 		}); | ||||
| 	} | ||||
| 
 | ||||
| 	public interface ReadInputsParams extends WorkParameters { | ||||
| 		Property<String> getTinyRemapperBuildServiceUuid(); | ||||
| 		Property<String> getInputTagName(); | ||||
| 		RegularFileProperty getInputFile(); | ||||
| 	} | ||||
| 
 | ||||
| 	public abstract static class ReadInputsAction implements WorkAction<ReadInputsParams> { | ||||
| 		private final TinyRemapperService tinyRemapperService; | ||||
| 
 | ||||
| 		public ReadInputsAction() { | ||||
| 			this.tinyRemapperService = UnsafeWorkQueueHelper.get(getParameters().getTinyRemapperBuildServiceUuid(), TinyRemapperService.class); | ||||
| 		} | ||||
| 
 | ||||
| 		@Override | ||||
| 		public void execute() { | ||||
| 			final TinyRemapper tinyRemapper = tinyRemapperService.getTinyRemapperForInputs(); | ||||
| 			final Path inputFile = getParameters().getInputFile().getAsFile().get().toPath(); | ||||
| 
 | ||||
| 			tinyRemapper.readInputsAsync(tinyRemapperService.createTag(getParameters().getInputTagName().get()), inputFile); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | @ -26,7 +26,6 @@ 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; | ||||
|  | @ -34,12 +33,14 @@ import java.util.Collection; | |||
| import java.util.List; | ||||
| import java.util.Map; | ||||
| import java.util.Objects; | ||||
| import java.util.function.Supplier; | ||||
| import java.util.jar.Manifest; | ||||
| import java.util.stream.Collectors; | ||||
| 
 | ||||
| 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; | ||||
| import org.gradle.api.file.ConfigurableFileCollection; | ||||
|  | @ -47,12 +48,11 @@ import org.gradle.api.file.FileCollection; | |||
| import org.gradle.api.plugins.JavaPlugin; | ||||
| import org.gradle.api.provider.ListProperty; | ||||
| import org.gradle.api.provider.Property; | ||||
| import org.gradle.api.provider.Provider; | ||||
| import org.gradle.api.tasks.Input; | ||||
| import org.gradle.api.tasks.InputFiles; | ||||
| import org.gradle.api.tasks.Internal; | ||||
| import org.gradle.api.tasks.SourceSet; | ||||
| import org.gradle.api.tasks.TaskAction; | ||||
| import org.objectweb.asm.commons.Remapper; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
| 
 | ||||
|  | @ -60,20 +60,18 @@ import net.fabricmc.accesswidener.AccessWidenerReader; | |||
| import net.fabricmc.accesswidener.AccessWidenerRemapper; | ||||
| import net.fabricmc.accesswidener.AccessWidenerWriter; | ||||
| import net.fabricmc.loom.LoomGradleExtension; | ||||
| import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; | ||||
| import net.fabricmc.loom.build.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.extension.MixinExtension; | ||||
| import net.fabricmc.loom.task.service.JarManifestService; | ||||
| import net.fabricmc.loom.task.service.MappingsService; | ||||
| import net.fabricmc.loom.task.service.TinyRemapperService; | ||||
| import net.fabricmc.loom.util.Constants; | ||||
| import net.fabricmc.loom.util.ZipUtils; | ||||
| import net.fabricmc.tinyremapper.InputTag; | ||||
| import net.fabricmc.loom.util.service.UnsafeWorkQueueHelper; | ||||
| import net.fabricmc.tinyremapper.OutputConsumerPath; | ||||
| import net.fabricmc.tinyremapper.TinyRemapper; | ||||
| import net.fabricmc.tinyremapper.TinyUtils; | ||||
| 
 | ||||
| public abstract class RemapJarTask extends AbstractRemapJarTask { | ||||
| 	private static final String MANIFEST_PATH = "META-INF/MANIFEST.MF"; | ||||
|  | @ -84,6 +82,8 @@ public abstract class RemapJarTask extends AbstractRemapJarTask { | |||
| 	@Input | ||||
| 	public abstract Property<Boolean> getAddNestedDependencies(); | ||||
| 
 | ||||
| 	private Supplier<TinyRemapperService> tinyRemapperService = Suppliers.memoize(() -> TinyRemapperService.getOrCreate(this)); | ||||
| 
 | ||||
| 	@Inject | ||||
| 	public RemapJarTask() { | ||||
| 		super(); | ||||
|  | @ -93,6 +93,25 @@ public abstract class RemapJarTask extends AbstractRemapJarTask { | |||
| 
 | ||||
| 		Configuration includeConfiguration = getProject().getConfigurations().getByName(Constants.Configurations.INCLUDE); | ||||
| 		getNestedJars().from(new IncludedJarFactory(getProject()).getNestedJars(includeConfiguration)); | ||||
| 
 | ||||
| 		setupPreparationTask(); | ||||
| 	} | ||||
| 
 | ||||
| 	private void setupPreparationTask() { | ||||
| 		PrepareJarRemapTask prepareJarTask = getProject().getTasks().create("prepare" + getName().substring(0, 1).toUpperCase() + getName().substring(1), PrepareJarRemapTask.class, this); | ||||
| 
 | ||||
| 		dependsOn(prepareJarTask); | ||||
| 		mustRunAfter(prepareJarTask); | ||||
| 
 | ||||
| 		getProject().getGradle().allprojects(project -> { | ||||
| 			project.getTasks().configureEach(task -> { | ||||
| 				if (task instanceof PrepareJarRemapTask otherTask) { | ||||
| 					// Ensure that all remap jars run after all prepare tasks | ||||
| 					dependsOn(otherTask); | ||||
| 					mustRunAfter(otherTask); | ||||
| 				} | ||||
| 			}); | ||||
| 		}); | ||||
| 	} | ||||
| 
 | ||||
| 	@TaskAction | ||||
|  | @ -105,14 +124,14 @@ public abstract class RemapJarTask extends AbstractRemapJarTask { | |||
| 			} | ||||
| 
 | ||||
| 			params.getJarManifestService().set(JarManifestService.get(getProject())); | ||||
| 			params.getTinyRemapperBuildServiceUuid().set(UnsafeWorkQueueHelper.create(getProject(), tinyRemapperService.get())); | ||||
| 			params.getRemapClasspath().from(getClasspath()); | ||||
| 			params.getMappings().add(MappingsService.createDefault(getProject(), getSourceNamespace().get(), getTargetNamespace().get())); | ||||
| 			params.getInputTagName().set(getInputTagName()); | ||||
| 
 | ||||
| 			final boolean legacyMixin = extension.getMixin().getUseLegacyMixinAp().get(); | ||||
| 			params.getUseMixinExtension().set(!legacyMixin); | ||||
| 
 | ||||
| 			if (legacyMixin) { | ||||
| 				params.getMixinMappings().from(extension.getAllMixinMappings()); | ||||
| 				setupLegacyMixinRefmapRemapping(params); | ||||
| 			} | ||||
| 		}); | ||||
|  | @ -174,8 +193,7 @@ public abstract class RemapJarTask extends AbstractRemapJarTask { | |||
| 	public interface RemapParams extends AbstractRemapParams { | ||||
| 		ConfigurableFileCollection getNestedJars(); | ||||
| 		ConfigurableFileCollection getRemapClasspath(); | ||||
| 		ConfigurableFileCollection getMixinMappings(); | ||||
| 		ListProperty<Provider<MappingsService>> getMappings(); | ||||
| 		Property<String> getInputTagName(); | ||||
| 
 | ||||
| 		Property<Boolean> getUseMixinExtension(); | ||||
| 
 | ||||
|  | @ -183,19 +201,25 @@ public abstract class RemapJarTask extends AbstractRemapJarTask { | |||
| 		ListProperty<RefmapData> getMixinData(); | ||||
| 
 | ||||
| 		Property<JarManifestService> getJarManifestService(); | ||||
| 		Property<String> getTinyRemapperBuildServiceUuid(); | ||||
| 	} | ||||
| 
 | ||||
| 	public abstract static class RemapAction extends AbstractRemapAction<RemapParams> { | ||||
| 		private static final Logger LOGGER = LoggerFactory.getLogger(RemapAction.class); | ||||
| 
 | ||||
| 		private final TinyRemapperService tinyRemapperService; | ||||
| 		private TinyRemapper tinyRemapper; | ||||
| 
 | ||||
| 		public RemapAction() { | ||||
| 			this.tinyRemapperService = UnsafeWorkQueueHelper.get(getParameters().getTinyRemapperBuildServiceUuid(), TinyRemapperService.class); | ||||
| 		} | ||||
| 
 | ||||
| 		@Override | ||||
| 		public void execute() { | ||||
| 			try { | ||||
| 				LOGGER.info("Remapping {} to {}", inputFile, outputFile); | ||||
| 
 | ||||
| 				tinyRemapper = createTinyRemapper(); | ||||
| 				tinyRemapper = tinyRemapperService.getTinyRemapperForRemapping(); | ||||
| 
 | ||||
| 				remap(); | ||||
| 				remapAccessWidener(); | ||||
|  | @ -204,9 +228,6 @@ public abstract class RemapJarTask extends AbstractRemapJarTask { | |||
| 				modifyJarManifest(); | ||||
| 				rewriteJar(); | ||||
| 
 | ||||
| 				tinyRemapper.finish(); | ||||
| 				tinyRemapper = null; | ||||
| 
 | ||||
| 				LOGGER.debug("Finished remapping {}", inputFile); | ||||
| 			} catch (Exception e) { | ||||
| 				try { | ||||
|  | @ -220,13 +241,9 @@ public abstract class RemapJarTask extends AbstractRemapJarTask { | |||
| 		} | ||||
| 
 | ||||
| 		private void remap() throws IOException { | ||||
| 			final InputTag inputTag = tinyRemapper.createInputTag(); | ||||
| 
 | ||||
| 			tinyRemapper.readInputsAsync(inputTag, inputFile); | ||||
| 
 | ||||
| 			try (OutputConsumerPath outputConsumer = new OutputConsumerPath.Builder(outputFile).build()) { | ||||
| 				outputConsumer.addNonClassFiles(inputFile); | ||||
| 				tinyRemapper.apply(outputConsumer, inputTag); | ||||
| 				tinyRemapper.apply(outputConsumer, tinyRemapperService.getTag(getParameters().getInputTagName().get())); | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
|  | @ -237,21 +254,21 @@ public abstract class RemapJarTask extends AbstractRemapJarTask { | |||
| 				return; | ||||
| 			} | ||||
| 
 | ||||
| 			byte[] remapped = remapAccessWidener(accessWidenerFile.content(), tinyRemapper.getEnvironment().getRemapper(), MappingsNamespace.INTERMEDIARY.toString()); | ||||
| 			byte[] remapped = remapAccessWidener(accessWidenerFile.content()); | ||||
| 
 | ||||
| 			// Finally, replace the output with the remaped aw | ||||
| 			ZipUtils.replace(outputFile, accessWidenerFile.path(), remapped); | ||||
| 		} | ||||
| 
 | ||||
| 		private static byte[] remapAccessWidener(byte[] input, Remapper asmRemapper, String targetNamespace) { | ||||
| 		private byte[] remapAccessWidener(byte[] input) { | ||||
| 			int version = AccessWidenerReader.readVersion(input); | ||||
| 
 | ||||
| 			AccessWidenerWriter writer = new AccessWidenerWriter(version); | ||||
| 			AccessWidenerRemapper remapper = new AccessWidenerRemapper( | ||||
| 					writer, | ||||
| 					asmRemapper, | ||||
| 					MappingsNamespace.NAMED.toString(), | ||||
| 					targetNamespace | ||||
| 					tinyRemapper.getEnvironment().getRemapper(), | ||||
| 					getParameters().getSourceNamespace().get(), | ||||
| 					getParameters().getTargetNamespace().get() | ||||
| 			); | ||||
| 			AccessWidenerReader reader = new AccessWidenerReader(remapper); | ||||
| 			reader.read(input); | ||||
|  | @ -300,30 +317,15 @@ public abstract class RemapJarTask extends AbstractRemapJarTask { | |||
| 				}))); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 		private TinyRemapper createTinyRemapper() { | ||||
| 			TinyRemapper.Builder builder = TinyRemapper.newRemapper(); | ||||
| 	@Internal | ||||
| 	public TinyRemapperService getTinyRemapperService() { | ||||
| 		return tinyRemapperService.get(); | ||||
| 	} | ||||
| 
 | ||||
| 			for (Provider<MappingsService> provider : getParameters().getMappings().get()) { | ||||
| 				builder.withMappings(provider.get().getMappingsProvider()); | ||||
| 			} | ||||
| 
 | ||||
| 			for (File mixinMapping : getParameters().getMixinMappings()) { | ||||
| 				builder.withMappings(TinyUtils.createTinyMappingProvider(mixinMapping.toPath(), getParameters().getSourceNamespace().get(), getParameters().getTargetNamespace().get())); | ||||
| 			} | ||||
| 
 | ||||
| 			if (getParameters().getUseMixinExtension().get()) { | ||||
| 				builder.extension(new net.fabricmc.tinyremapper.extension.mixin.MixinExtension()); | ||||
| 			} | ||||
| 
 | ||||
| 			TinyRemapper remapper = builder.build(); | ||||
| 
 | ||||
| 			// Apply classpath | ||||
| 			for (File file : getParameters().getRemapClasspath()) { | ||||
| 				remapper.readClassPathAsync(file.toPath()); | ||||
| 			} | ||||
| 
 | ||||
| 			return remapper; | ||||
| 		} | ||||
| 	@Internal | ||||
| 	String getInputTagName() { | ||||
| 		return getProject().getPath() + getName(); | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -35,8 +35,8 @@ import org.gradle.api.tasks.TaskAction; | |||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
| 
 | ||||
| import net.fabricmc.loom.task.service.MappingsService; | ||||
| import net.fabricmc.loom.task.service.SourceRemapperService; | ||||
| import net.fabricmc.loom.util.service.UnsafeWorkQueueHelper; | ||||
| 
 | ||||
| public abstract class RemapSourcesJarTask extends AbstractRemapJarTask { | ||||
| 	@Inject | ||||
|  | @ -49,12 +49,12 @@ public abstract class RemapSourcesJarTask extends AbstractRemapJarTask { | |||
| 	@TaskAction | ||||
| 	public void run() { | ||||
| 		submitWork(RemapSourcesAction.class, params -> { | ||||
| 			params.getSourcesRemapperService().set(SourceRemapperService.create(getProject(), MappingsService.createDefault(getProject(), getSourceNamespace().get(), getTargetNamespace().get()), getClasspath())); | ||||
| 			params.getSourcesRemapperServiceUuid().set(UnsafeWorkQueueHelper.create(getProject(), SourceRemapperService.create(this))); | ||||
| 		}); | ||||
| 	} | ||||
| 
 | ||||
| 	public interface RemapSourcesParams extends AbstractRemapParams { | ||||
| 		Property<SourceRemapperService> getSourcesRemapperService(); | ||||
| 		Property<String> getSourcesRemapperServiceUuid(); | ||||
| 	} | ||||
| 
 | ||||
| 	public abstract static class RemapSourcesAction extends AbstractRemapAction<RemapSourcesParams> { | ||||
|  | @ -65,7 +65,7 @@ public abstract class RemapSourcesJarTask extends AbstractRemapJarTask { | |||
| 		public RemapSourcesAction() { | ||||
| 			super(); | ||||
| 
 | ||||
| 			sourceRemapperService = getParameters().getSourcesRemapperService().get(); | ||||
| 			sourceRemapperService = UnsafeWorkQueueHelper.get(getParameters().getSourcesRemapperServiceUuid(), SourceRemapperService.class); | ||||
| 		} | ||||
| 
 | ||||
| 		@Override | ||||
|  |  | |||
|  | @ -54,8 +54,8 @@ public class RemapTaskConfiguration { | |||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
| 		// Register the default remap jar task | ||||
| 		TaskProvider<RemapJarTask> remapJarTaskProvider = tasks.register(REMAP_JAR_TASK_NAME, RemapJarTask.class, task -> { | ||||
| 		// Register the default remap jar task - must not be lazy to ensure that the prepare tasks get setup for other projects to depend on. | ||||
| 		RemapJarTask remapJarTask = tasks.create(REMAP_JAR_TASK_NAME, RemapJarTask.class, task -> { | ||||
| 			final AbstractArchiveTask jarTask = tasks.named(JavaPlugin.JAR_TASK_NAME, AbstractArchiveTask.class).get(); | ||||
| 
 | ||||
| 			// Basic task setup | ||||
|  | @ -76,7 +76,7 @@ public class RemapTaskConfiguration { | |||
| 			task.getDestinationDirectory().set(new File(project.getBuildDir(), "devlibs")); | ||||
| 		}); | ||||
| 
 | ||||
| 		tasks.named(BasePlugin.ASSEMBLE_TASK_NAME).configure(task -> task.dependsOn(remapJarTaskProvider)); | ||||
| 		tasks.named(BasePlugin.ASSEMBLE_TASK_NAME).configure(task -> task.dependsOn(remapJarTask)); | ||||
| 
 | ||||
| 		trySetupSourceRemapping(project); | ||||
| 
 | ||||
|  |  | |||
|  | @ -24,49 +24,45 @@ | |||
| 
 | ||||
| package net.fabricmc.loom.task.service; | ||||
| 
 | ||||
| import java.io.File; | ||||
| import java.io.IOException; | ||||
| import java.io.UncheckedIOException; | ||||
| import java.nio.file.Path; | ||||
| 
 | ||||
| import org.gradle.api.Project; | ||||
| import org.gradle.api.file.RegularFileProperty; | ||||
| import org.gradle.api.provider.Property; | ||||
| import org.gradle.api.provider.Provider; | ||||
| import org.gradle.api.services.BuildService; | ||||
| import org.gradle.api.services.BuildServiceParameters; | ||||
| 
 | ||||
| import net.fabricmc.loom.LoomGradleExtension; | ||||
| import net.fabricmc.loom.configuration.providers.mappings.MappingsProviderImpl; | ||||
| import net.fabricmc.loom.util.TinyRemapperHelper; | ||||
| import net.fabricmc.loom.util.service.SharedService; | ||||
| import net.fabricmc.loom.util.service.SharedServiceManager; | ||||
| import net.fabricmc.mappingio.MappingReader; | ||||
| import net.fabricmc.mappingio.tree.MemoryMappingTree; | ||||
| import net.fabricmc.tinyremapper.IMappingProvider; | ||||
| 
 | ||||
| public abstract class MappingsService implements BuildService<MappingsService.Params>, AutoCloseable { | ||||
| 	interface Params extends BuildServiceParameters { | ||||
| 		RegularFileProperty getMappingsFile(); | ||||
| public final class MappingsService implements SharedService { | ||||
| 	private record Options(Path mappingsFile, String from, String to, boolean remapLocals) { } | ||||
| 
 | ||||
| 		Property<String> getFromNamespace(); | ||||
| 		Property<String> getToNamespace(); | ||||
| 
 | ||||
| 		Property<Boolean> getRemapLocals(); | ||||
| 	public static MappingsService create(Project project, String name, Path mappingsFile, String from, String to, boolean remapLocals) { | ||||
| 		return create(SharedServiceManager.get(project), name, mappingsFile, from, to, remapLocals); | ||||
| 	} | ||||
| 
 | ||||
| 	public static synchronized Provider<MappingsService> create(Project project, String name, File mappingsFile, String from, String to, boolean remapLocals) { | ||||
| 		return project.getGradle().getSharedServices().registerIfAbsent(name, MappingsService.class, spec -> { | ||||
| 			spec.parameters(params -> { | ||||
| 				params.getMappingsFile().set(mappingsFile); | ||||
| 				params.getFromNamespace().set(from); | ||||
| 				params.getToNamespace().set(to); | ||||
| 				params.getRemapLocals().set(remapLocals); | ||||
| 			}); | ||||
| 		}); | ||||
| 	public static synchronized MappingsService create(SharedServiceManager sharedServiceManager, String name, Path mappingsFile, String from, String to, boolean remapLocals) { | ||||
| 		final Options options = new Options(mappingsFile, from, to, remapLocals); | ||||
| 		final String id = name + options.hashCode(); | ||||
| 		return sharedServiceManager.getOrCreateService(id, () -> new MappingsService(options)); | ||||
| 	} | ||||
| 
 | ||||
| 	public static Provider<MappingsService> createDefault(Project project, String from, String to) { | ||||
| 	public static MappingsService createDefault(Project project, String from, String to) { | ||||
| 		final MappingsProviderImpl mappingsProvider = LoomGradleExtension.get(project).getMappingsProvider(); | ||||
| 
 | ||||
| 		final String name = mappingsProvider.getBuildServiceName("mappingsProvider", from, to); | ||||
| 		return MappingsService.create(project, name, mappingsProvider.tinyMappings.toFile(), from, to, false); | ||||
| 		return MappingsService.create(project, name, mappingsProvider.tinyMappings, from, to, false); | ||||
| 	} | ||||
| 
 | ||||
| 	private final Options options; | ||||
| 
 | ||||
| 	public MappingsService(Options options) { | ||||
| 		this.options = options; | ||||
| 	} | ||||
| 
 | ||||
| 	private IMappingProvider mappingProvider = null; | ||||
|  | @ -76,13 +72,13 @@ public abstract class MappingsService implements BuildService<MappingsService.Pa | |||
| 		if (mappingProvider == null) { | ||||
| 			try { | ||||
| 				mappingProvider = TinyRemapperHelper.create( | ||||
| 						getParameters().getMappingsFile().get().getAsFile().toPath(), | ||||
| 						getParameters().getFromNamespace().get(), | ||||
| 						getParameters().getToNamespace().get(), | ||||
| 						getParameters().getRemapLocals().get() | ||||
| 						options.mappingsFile(), | ||||
| 						options.from(), | ||||
| 						options.to(), | ||||
| 						options.remapLocals() | ||||
| 				); | ||||
| 			} catch (IOException e) { | ||||
| 				throw new UncheckedIOException("Failed to read mappings from: " + getParameters().getMappingsFile().get().getAsFile().getAbsolutePath(), e); | ||||
| 				throw new UncheckedIOException("Failed to read mappings from: " + options.mappingsFile(), e); | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
|  | @ -94,9 +90,9 @@ public abstract class MappingsService implements BuildService<MappingsService.Pa | |||
| 			memoryMappingTree = new MemoryMappingTree(); | ||||
| 
 | ||||
| 			try { | ||||
| 				MappingReader.read(getParameters().getMappingsFile().get().getAsFile().toPath(), memoryMappingTree); | ||||
| 				MappingReader.read(options.mappingsFile(), memoryMappingTree); | ||||
| 			} catch (IOException e) { | ||||
| 				throw new UncheckedIOException("Failed to read mappings from: " + getParameters().getMappingsFile().get().getAsFile().getAbsolutePath(), e); | ||||
| 				throw new UncheckedIOException("Failed to read mappings from: " + options.mappingsFile(), e); | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
|  | @ -104,11 +100,11 @@ public abstract class MappingsService implements BuildService<MappingsService.Pa | |||
| 	} | ||||
| 
 | ||||
| 	public String getFromNamespace() { | ||||
| 		return getParameters().getFromNamespace().get(); | ||||
| 		return options.from(); | ||||
| 	} | ||||
| 
 | ||||
| 	public String getToNamespace() { | ||||
| 		return getParameters().getToNamespace().get(); | ||||
| 		return options.to(); | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
|  |  | |||
|  | @ -0,0 +1,69 @@ | |||
| /* | ||||
|  * 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.task.service; | ||||
| 
 | ||||
| import java.io.File; | ||||
| import java.util.HashSet; | ||||
| 
 | ||||
| import org.gradle.api.Project; | ||||
| import org.gradle.api.tasks.SourceSet; | ||||
| 
 | ||||
| import net.fabricmc.loom.LoomGradleExtension; | ||||
| import net.fabricmc.loom.util.service.SharedService; | ||||
| import net.fabricmc.loom.util.service.SharedServiceManager; | ||||
| import net.fabricmc.tinyremapper.IMappingProvider; | ||||
| 
 | ||||
| public final class MixinMappingsService implements SharedService { | ||||
| 	private final SharedServiceManager sharedServiceManager; | ||||
| 	private final HashSet<File> mixinMappings = new HashSet<>(); | ||||
| 
 | ||||
| 	private MixinMappingsService(SharedServiceManager sharedServiceManager) { | ||||
| 		this.sharedServiceManager = sharedServiceManager; | ||||
| 	} | ||||
| 
 | ||||
| 	public static File getMixinMappingFile(Project project, SourceSet sourceSet) { | ||||
| 		final LoomGradleExtension extension = LoomGradleExtension.get(project); | ||||
| 		File mixinMapping = new File(extension.getFiles().getProjectBuildCache(), "mixin-map-" + extension.getMappingsProvider().mappingsIdentifier() + "." + sourceSet.getName() + ".tiny"); | ||||
| 
 | ||||
| 		getService(SharedServiceManager.get(project)).mixinMappings.add(mixinMapping); | ||||
| 
 | ||||
| 		return mixinMapping; | ||||
| 	} | ||||
| 
 | ||||
| 	static MixinMappingsService getService(SharedServiceManager sharedServiceManager) { | ||||
| 		return sharedServiceManager.getOrCreateService("MixinMappings", () -> new MixinMappingsService(sharedServiceManager)); | ||||
| 	} | ||||
| 
 | ||||
| 	IMappingProvider getMappingProvider(String from, String to) { | ||||
| 		return out -> { | ||||
| 			for (File mixinMapping : mixinMappings) { | ||||
| 				if (!mixinMapping.exists()) continue; | ||||
| 
 | ||||
| 				MappingsService service = MappingsService.create(sharedServiceManager, mixinMapping.getAbsolutePath(), mixinMapping.toPath(), from, to, false); | ||||
| 				service.getMappingsProvider().load(out); | ||||
| 			} | ||||
| 		}; | ||||
| 	} | ||||
| } | ||||
|  | @ -24,49 +24,57 @@ | |||
| 
 | ||||
| package net.fabricmc.loom.task.service; | ||||
| 
 | ||||
| import java.io.File; | ||||
| import java.io.IOException; | ||||
| import java.io.UncheckedIOException; | ||||
| import java.nio.file.Files; | ||||
| import java.nio.file.Path; | ||||
| import java.util.function.Supplier; | ||||
| 
 | ||||
| import com.google.common.base.Suppliers; | ||||
| import org.cadixdev.lorenz.MappingSet; | ||||
| import org.cadixdev.mercury.Mercury; | ||||
| import org.cadixdev.mercury.remapper.MercuryRemapper; | ||||
| import org.gradle.api.Project; | ||||
| import org.gradle.api.file.ConfigurableFileCollection; | ||||
| import org.gradle.api.file.FileCollection; | ||||
| import org.gradle.api.provider.Property; | ||||
| import org.gradle.api.provider.Provider; | ||||
| import org.gradle.api.services.BuildService; | ||||
| import org.gradle.api.services.BuildServiceParameters; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
| 
 | ||||
| import net.fabricmc.loom.LoomGradleExtension; | ||||
| import net.fabricmc.loom.task.RemapSourcesJarTask; | ||||
| import net.fabricmc.loom.util.DeletingFileVisitor; | ||||
| import net.fabricmc.loom.util.FileSystemUtil; | ||||
| import net.fabricmc.loom.util.SourceRemapper; | ||||
| import net.fabricmc.loom.util.ZipUtils; | ||||
| import net.fabricmc.loom.util.service.SharedService; | ||||
| import net.fabricmc.loom.util.service.SharedServiceManager; | ||||
| import net.fabricmc.lorenztiny.TinyMappingsReader; | ||||
| 
 | ||||
| public abstract class SourceRemapperService implements BuildService<SourceRemapperService.Params>, AutoCloseable { | ||||
| 	public interface Params extends BuildServiceParameters { | ||||
| 		Property<Provider<MappingsService>> getMappings(); | ||||
| public final class SourceRemapperService implements SharedService { | ||||
| 	public static synchronized SourceRemapperService create(RemapSourcesJarTask task) { | ||||
| 		final Project project = task.getProject(); | ||||
| 		final String to = task.getTargetNamespace().get(); | ||||
| 		final String from = task.getSourceNamespace().get(); | ||||
| 		final LoomGradleExtension extension = LoomGradleExtension.get(project); | ||||
| 		final SharedServiceManager sharedServiceManager = SharedServiceManager.get(project); | ||||
| 		final String id = extension.getMappingsProvider().getBuildServiceName("sourceremapper", from, to); | ||||
| 
 | ||||
| 		ConfigurableFileCollection getClasspath(); | ||||
| 	} | ||||
| 
 | ||||
| 	public static synchronized Provider<SourceRemapperService> create(Project project, Provider<MappingsService> mappings, FileCollection classpath) { | ||||
| 		// TODO may need a better name, im not too sure | ||||
| 		return project.getGradle().getSharedServices().registerIfAbsent("sourceremapper", SourceRemapperService.class, spec -> | ||||
| 			spec.parameters(params -> { | ||||
| 				params.getMappings().set(mappings); | ||||
| 				params.getClasspath().from(classpath); | ||||
| 			} | ||||
| 		)); | ||||
| 		return sharedServiceManager.getOrCreateService(id, () -> | ||||
| 				new SourceRemapperService(MappingsService.createDefault(project, from, to), task.getClasspath() | ||||
| 			)); | ||||
| 	} | ||||
| 
 | ||||
| 	private static final Logger LOGGER = LoggerFactory.getLogger(SourceRemapperService.class); | ||||
| 
 | ||||
| 	private Mercury mercury; | ||||
| 	private final MappingsService mappingsService; | ||||
| 	private final ConfigurableFileCollection classpath; | ||||
| 
 | ||||
| 	private final Supplier<Mercury> mercury = Suppliers.memoize(this::createMercury); | ||||
| 
 | ||||
| 	private SourceRemapperService(MappingsService mappingsService, ConfigurableFileCollection classpath) { | ||||
| 		this.mappingsService = mappingsService; | ||||
| 		this.classpath = classpath; | ||||
| 	} | ||||
| 
 | ||||
| 	public void remapSourcesJar(Path source, Path destination) throws IOException { | ||||
| 		if (source.equals(destination)) { | ||||
|  | @ -99,35 +107,34 @@ public abstract class SourceRemapperService implements BuildService<SourceRemapp | |||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	private synchronized void doRemap(Path srcPath, Path dstPath, Path source) throws IOException { | ||||
| 		if (mercury == null) { | ||||
| 			mercury = new Mercury(); | ||||
| 			mercury.setGracefulClasspathChecks(true); | ||||
| 			mercury.getProcessors().add(MercuryRemapper.create(getMappings())); | ||||
| 
 | ||||
| 			getParameters().getClasspath().forEach(file -> mercury.getClassPath().add(file.toPath())); | ||||
| 		} | ||||
| 
 | ||||
| 	private synchronized void doRemap(Path srcPath, Path dstPath, Path source) { | ||||
| 		try { | ||||
| 			// Not thread safe!! | ||||
| 			mercury.rewrite(srcPath, dstPath); | ||||
| 			synchronized (mercury) { | ||||
| 				mercury.get().rewrite(srcPath, dstPath); | ||||
| 			} | ||||
| 		} catch (Exception e) { | ||||
| 			LOGGER.warn("Could not remap " + source + " fully!", e); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	private MappingSet getMappings() throws IOException { | ||||
| 		return new TinyMappingsReader(mappingsService().getMemoryMappingTree(), mappingsService().getFromNamespace(), mappingsService().getToNamespace()).read(); | ||||
| 		return new TinyMappingsReader(mappingsService.getMemoryMappingTree(), mappingsService.getFromNamespace(), mappingsService.getToNamespace()).read(); | ||||
| 	} | ||||
| 
 | ||||
| 	private MappingsService mappingsService() { | ||||
| 		return getParameters().getMappings().get().get(); | ||||
| 	} | ||||
| 	private Mercury createMercury() { | ||||
| 		var mercury = new Mercury(); | ||||
| 		mercury.setGracefulClasspathChecks(true); | ||||
| 
 | ||||
| 	@Override | ||||
| 	public void close() throws Exception { | ||||
| 		mercury = null; | ||||
| 		// This is required (: | ||||
| 		System.gc(); | ||||
| 		try { | ||||
| 			mercury.getProcessors().add(MercuryRemapper.create(getMappings())); | ||||
| 		} catch (IOException e) { | ||||
| 			throw new UncheckedIOException("Failed to read mercury mappings", e); | ||||
| 		} | ||||
| 
 | ||||
| 		for (File file : classpath.getFiles()) { | ||||
| 			mercury.getClassPath().add(file.toPath()); | ||||
| 		} | ||||
| 
 | ||||
| 		return mercury; | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -0,0 +1,142 @@ | |||
| /* | ||||
|  * 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.task.service; | ||||
| 
 | ||||
| import java.io.File; | ||||
| import java.io.IOException; | ||||
| import java.nio.file.Path; | ||||
| import java.util.ArrayList; | ||||
| import java.util.HashSet; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
| import java.util.Objects; | ||||
| import java.util.concurrent.ConcurrentHashMap; | ||||
| 
 | ||||
| import org.gradle.api.Project; | ||||
| 
 | ||||
| import net.fabricmc.loom.LoomGradleExtension; | ||||
| import net.fabricmc.loom.task.AbstractRemapJarTask; | ||||
| import net.fabricmc.loom.util.service.SharedService; | ||||
| import net.fabricmc.loom.util.service.SharedServiceManager; | ||||
| import net.fabricmc.tinyremapper.IMappingProvider; | ||||
| import net.fabricmc.tinyremapper.InputTag; | ||||
| import net.fabricmc.tinyremapper.TinyRemapper; | ||||
| 
 | ||||
| public class TinyRemapperService implements SharedService { | ||||
| 	public static synchronized TinyRemapperService getOrCreate(AbstractRemapJarTask remapJarTask) { | ||||
| 		final Project project = remapJarTask.getProject(); | ||||
| 		final String to = remapJarTask.getTargetNamespace().get(); | ||||
| 		final String from = remapJarTask.getSourceNamespace().get(); | ||||
| 		final LoomGradleExtension extension = LoomGradleExtension.get(project); | ||||
| 		final SharedServiceManager sharedServiceManager = SharedServiceManager.get(project); | ||||
| 		final boolean legacyMixin = extension.getMixin().getUseLegacyMixinAp().get(); | ||||
| 
 | ||||
| 		// Generates an id that is used to share the remapper across projects. This tasks in the remap jar task name to handle custom remap jar tasks separately. | ||||
| 		final String id = extension.getMappingsProvider().getBuildServiceName("remapJarService", from, to) + ":" + remapJarTask.getName(); | ||||
| 
 | ||||
| 		TinyRemapperService service = sharedServiceManager.getOrCreateService(id, () -> { | ||||
| 			List<IMappingProvider> mappings = new ArrayList<>(); | ||||
| 			mappings.add(MappingsService.createDefault(project, from, to).getMappingsProvider()); | ||||
| 
 | ||||
| 			if (legacyMixin) { | ||||
| 				mappings.add(MixinMappingsService.getService(SharedServiceManager.get(project)).getMappingProvider(from, to)); | ||||
| 			} | ||||
| 
 | ||||
| 			return new TinyRemapperService(mappings, !legacyMixin); | ||||
| 		}); | ||||
| 
 | ||||
| 		service.readClasspath(remapJarTask.getClasspath().getFiles().stream().map(File::toPath).toList()); | ||||
| 
 | ||||
| 		return service; | ||||
| 	} | ||||
| 
 | ||||
| 	private TinyRemapper tinyRemapper; | ||||
| 	private final Map<String, InputTag> inputTagMap = new ConcurrentHashMap<>(); | ||||
| 	private final HashSet<Path> classpath = new HashSet<>(); | ||||
| 	// Set to true once remapping has started, once set no inputs can be read. | ||||
| 	private boolean isRemapping = false; | ||||
| 
 | ||||
| 	public TinyRemapperService(List<IMappingProvider> mappings, boolean useMixinExtension) { | ||||
| 		TinyRemapper.Builder builder = TinyRemapper.newRemapper(); | ||||
| 
 | ||||
| 		for (IMappingProvider provider : mappings) { | ||||
| 			builder.withMappings(provider); | ||||
| 		} | ||||
| 
 | ||||
| 		if (useMixinExtension) { | ||||
| 			builder.extension(new net.fabricmc.tinyremapper.extension.mixin.MixinExtension()); | ||||
| 		} | ||||
| 
 | ||||
| 		tinyRemapper = builder.build(); | ||||
| 	} | ||||
| 
 | ||||
| 	public InputTag createTag(String key) { | ||||
| 		if (inputTagMap.containsKey(key)) { | ||||
| 			throw new IllegalStateException("Input tag already exists for key: " + key); | ||||
| 		} | ||||
| 
 | ||||
| 		return inputTagMap.put(key, tinyRemapper.createInputTag()); | ||||
| 	} | ||||
| 
 | ||||
| 	public InputTag getTag(String key) { | ||||
| 		return Objects.requireNonNull(inputTagMap.get(key), "Input tag not found for: " + key); | ||||
| 	} | ||||
| 
 | ||||
| 	public TinyRemapper getTinyRemapperForRemapping() { | ||||
| 		synchronized (this) { | ||||
| 			isRemapping = true; | ||||
| 			return Objects.requireNonNull(tinyRemapper, "Tiny remapper has not been setup"); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	public synchronized TinyRemapper getTinyRemapperForInputs() { | ||||
| 		synchronized (this) { | ||||
| 			if (isRemapping) { | ||||
| 				throw new IllegalStateException("Cannot read inputs as remapping has already started"); | ||||
| 			} | ||||
| 
 | ||||
| 			return tinyRemapper; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	void readClasspath(List<Path> paths) { | ||||
| 		List<Path> toRead; | ||||
| 
 | ||||
| 		synchronized (classpath) { | ||||
| 			toRead = paths.stream().filter(path -> !classpath.contains(path)).toList(); | ||||
| 			classpath.addAll(paths); | ||||
| 		} | ||||
| 
 | ||||
| 		tinyRemapper.readClassPathAsync(toRead.toArray(Path[]::new)); | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public void close() throws IOException { | ||||
| 		if (tinyRemapper != null) { | ||||
| 			tinyRemapper.finish(); | ||||
| 			tinyRemapper = null; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | @ -35,7 +35,7 @@ import java.util.zip.GZIPInputStream; | |||
| import com.google.common.io.Files; | ||||
| import org.apache.commons.io.FileUtils; | ||||
| import org.gradle.api.Project; | ||||
| import org.gradle.api.logging.Logger; | ||||
| import org.slf4j.Logger; | ||||
| 
 | ||||
| import net.fabricmc.loom.LoomGradlePlugin; | ||||
| 
 | ||||
|  |  | |||
|  | @ -110,21 +110,31 @@ public final class TinyRemapperHelper { | |||
| 
 | ||||
| 	public static IMappingProvider create(MappingTree mappings, String from, String to, boolean remapLocalVariables) { | ||||
| 		return (acceptor) -> { | ||||
| 			final int fromId = mappings.getNamespaceId(from); | ||||
| 			final int toId = mappings.getNamespaceId(to); | ||||
| 
 | ||||
| 			for (MappingTree.ClassMapping classDef : mappings.getClasses()) { | ||||
| 				String className = classDef.getName(from); | ||||
| 				acceptor.acceptClass(className, classDef.getName(to)); | ||||
| 				String className = classDef.getName(fromId); | ||||
| 				String dstName = classDef.getName(toId); | ||||
| 
 | ||||
| 				if (dstName == null) { | ||||
| 					// Unsure if this is correct, should be better than crashing tho. | ||||
| 					dstName = className; | ||||
| 				} | ||||
| 
 | ||||
| 				acceptor.acceptClass(className, dstName); | ||||
| 
 | ||||
| 				for (MappingTree.FieldMapping field : classDef.getFields()) { | ||||
| 					acceptor.acceptField(memberOf(className, field.getName(from), field.getDesc(from)), field.getName(to)); | ||||
| 					acceptor.acceptField(memberOf(className, field.getName(fromId), field.getDesc(fromId)), field.getName(toId)); | ||||
| 				} | ||||
| 
 | ||||
| 				for (MappingTree.MethodMapping method : classDef.getMethods()) { | ||||
| 					IMappingProvider.Member methodIdentifier = memberOf(className, method.getName(from), method.getDesc(from)); | ||||
| 					acceptor.acceptMethod(methodIdentifier, method.getName(to)); | ||||
| 					IMappingProvider.Member methodIdentifier = memberOf(className, method.getName(fromId), method.getDesc(fromId)); | ||||
| 					acceptor.acceptMethod(methodIdentifier, method.getName(toId)); | ||||
| 
 | ||||
| 					if (remapLocalVariables) { | ||||
| 						for (MappingTree.MethodArgMapping parameter : method.getArgs()) { | ||||
| 							String name = parameter.getName(to); | ||||
| 							String name = parameter.getName(toId); | ||||
| 
 | ||||
| 							if (name == null) { | ||||
| 								continue; | ||||
|  | @ -136,7 +146,7 @@ public final class TinyRemapperHelper { | |||
| 						for (MappingTree.MethodVarMapping localVariable : method.getVars()) { | ||||
| 							acceptor.acceptMethodVar(methodIdentifier, localVariable.getLvIndex(), | ||||
| 									localVariable.getStartOpIdx(), localVariable.getLvtRowIndex(), | ||||
| 									localVariable.getName(to)); | ||||
| 									localVariable.getName(toId)); | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
|  |  | |||
|  | @ -0,0 +1,34 @@ | |||
| /* | ||||
|  * 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.util.service; | ||||
| 
 | ||||
| import java.io.Closeable; | ||||
| import java.io.IOException; | ||||
| 
 | ||||
| public interface SharedService extends Closeable { | ||||
| 	@Override | ||||
| 	default void close() throws IOException { | ||||
| 	} | ||||
| } | ||||
|  | @ -0,0 +1,110 @@ | |||
| /* | ||||
|  * 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.util.service; | ||||
| 
 | ||||
| import java.io.IOException; | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
| import java.util.concurrent.ConcurrentHashMap; | ||||
| import java.util.function.Supplier; | ||||
| 
 | ||||
| import org.gradle.BuildResult; | ||||
| import org.gradle.api.Project; | ||||
| import org.gradle.api.invocation.Gradle; | ||||
| 
 | ||||
| /** | ||||
|  * A simple manager for {@link SharedService} to be used across gradle (sub) projects. | ||||
|  * This is a basic replacement for gradle's build service api. | ||||
|  */ | ||||
| public final class SharedServiceManager { | ||||
| 	private static final Map<Gradle, SharedServiceManager> SERVICE_FACTORY_MAP = new ConcurrentHashMap<>(); | ||||
| 	private final Gradle gradle; | ||||
| 
 | ||||
| 	private final Map<String, SharedService> sharedServiceMap = new ConcurrentHashMap<>(); | ||||
| 
 | ||||
| 	private boolean shutdown = false; | ||||
| 
 | ||||
| 	private SharedServiceManager(Gradle gradle) { | ||||
| 		this.gradle = gradle; | ||||
| 		this.gradle.buildFinished(this::onFinish); | ||||
| 	} | ||||
| 
 | ||||
| 	public static SharedServiceManager get(Project project) { | ||||
| 		return get(project.getGradle()); | ||||
| 	} | ||||
| 
 | ||||
| 	public static SharedServiceManager get(Gradle gradle) { | ||||
| 		return SERVICE_FACTORY_MAP.computeIfAbsent(gradle, SharedServiceManager::new); | ||||
| 	} | ||||
| 
 | ||||
| 	public <S extends SharedService> S getOrCreateService(String id, Supplier<S> function) { | ||||
| 		synchronized (sharedServiceMap) { | ||||
| 			if (shutdown) { | ||||
| 				throw new UnsupportedOperationException("Cannot get or create service has the manager has been shutdown."); | ||||
| 			} | ||||
| 
 | ||||
| 			//noinspection unchecked | ||||
| 			S sharedService = (S) sharedServiceMap.get(id); | ||||
| 
 | ||||
| 			if (sharedService == null) { | ||||
| 				sharedService = function.get(); | ||||
| 				sharedServiceMap.put(id, sharedService); | ||||
| 			} | ||||
| 
 | ||||
| 			return sharedService; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	private void onFinish(BuildResult buildResult) { | ||||
| 		synchronized (sharedServiceMap) { | ||||
| 			shutdown = true; | ||||
| 		} | ||||
| 
 | ||||
| 		SERVICE_FACTORY_MAP.remove(gradle); | ||||
| 
 | ||||
| 		final List<IOException> exceptionList = new ArrayList<>(); | ||||
| 
 | ||||
| 		for (SharedService sharedService : sharedServiceMap.values()) { | ||||
| 			try { | ||||
| 				sharedService.close(); | ||||
| 			} catch (IOException e) { | ||||
| 				exceptionList.add(e); | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		sharedServiceMap.clear(); | ||||
| 
 | ||||
| 		if (!exceptionList.isEmpty()) { | ||||
| 			// Done to try and close all the services. | ||||
| 			RuntimeException exception = new RuntimeException("Failed to close all shared services"); | ||||
| 			exceptionList.forEach(exception::addSuppressed); | ||||
| 			throw exception; | ||||
| 		} | ||||
| 
 | ||||
| 		// This is required to ensure that mercury releases all of the file handles. | ||||
| 		System.gc(); | ||||
| 	} | ||||
| } | ||||
|  | @ -0,0 +1,60 @@ | |||
| /* | ||||
|  * 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.util.service; | ||||
| 
 | ||||
| import java.util.Map; | ||||
| import java.util.UUID; | ||||
| import java.util.concurrent.ConcurrentHashMap; | ||||
| 
 | ||||
| import org.gradle.api.Project; | ||||
| import org.gradle.api.provider.Property; | ||||
| 
 | ||||
| // Massive hack to work around WorkerExecutor.noIsolation() doing isolation checks. | ||||
| public final class UnsafeWorkQueueHelper { | ||||
| 	private static final Map<String, SharedService> SERVICE_MAP = new ConcurrentHashMap<>(); | ||||
| 
 | ||||
| 	private UnsafeWorkQueueHelper() { | ||||
| 	} | ||||
| 
 | ||||
| 	public static String create(Project project, SharedService service) { | ||||
| 		final String uuid = UUID.randomUUID().toString(); | ||||
| 		SERVICE_MAP.put(uuid, service); | ||||
| 
 | ||||
| 		// Ensure we don't make a mess if things go wrong. | ||||
| 		project.getGradle().buildFinished(buildResult -> SERVICE_MAP.remove(uuid)); | ||||
| 		return uuid; | ||||
| 	} | ||||
| 
 | ||||
| 	public static <S> S get(Property<String> property, Class<S> clazz) { | ||||
| 		SharedService service = SERVICE_MAP.remove(property.get()); | ||||
| 
 | ||||
| 		if (service == null) { | ||||
| 			throw new NullPointerException("Failed to get service for " + clazz); | ||||
| 		} | ||||
| 
 | ||||
| 		//noinspection unchecked | ||||
| 		return (S) service; | ||||
| 	} | ||||
| } | ||||
|  | @ -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-20220101231120+0000" | ||||
|     public final static String PRE_RELEASE_GRADLE = "7.5-20220110230252+0000" | ||||
| 
 | ||||
|     public final static String[] STANDARD_TEST_VERSIONS = [DEFAULT_GRADLE, PRE_RELEASE_GRADLE] | ||||
| } | ||||
|  |  | |||
|  | @ -0,0 +1,63 @@ | |||
| /* | ||||
|  * 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.benchmark | ||||
| 
 | ||||
| import groovy.time.TimeCategory | ||||
| import groovy.time.TimeDuration | ||||
| import net.fabricmc.loom.test.LoomTestConstants | ||||
| import net.fabricmc.loom.test.util.GradleProjectTestTrait | ||||
| 
 | ||||
| /** | ||||
|  * Run this class, passing a working dir as the first argument. | ||||
|  * Allow for one warm up run before profiling, follow up runs should not be using the network. | ||||
|  */ | ||||
| @Singleton | ||||
| class FabricAPIBenchmark implements GradleProjectTestTrait { | ||||
|     def run(File dir) { | ||||
|         def gradle = gradleProject( | ||||
|                 version: LoomTestConstants.PRE_RELEASE_GRADLE, | ||||
|                 projectDir: new File(dir, "project"), | ||||
|                 gradleHomeDir: new File(dir, "gradlehome"), | ||||
|                 allowExistingRepo: true, | ||||
| 
 | ||||
|                 repo: "https://github.com/FabricMC/fabric.git", | ||||
|                 commit: "71b634e5b7845296b11be3fa6545f4fbfacc017f", | ||||
|                 patch: "fabric_api" | ||||
|         ) | ||||
| 
 | ||||
|         def timeStart = new Date() | ||||
| 
 | ||||
|         def result = gradle.run(tasks: ["clean", "build"], args: ["--parallel", "-x", "check", "-x", "test", "-x", ":fabric-data-generation-api-v1:runDatagen", "-x", "javadoc"]) | ||||
| 
 | ||||
|         def timeStop = new Date() | ||||
|         TimeDuration duration = TimeCategory.minus(timeStop, timeStart) | ||||
|         println(duration) | ||||
|     } | ||||
| 
 | ||||
|     static void main(String[] args) { | ||||
|         getInstance().run(new File(args[0])) | ||||
|         System.exit(0) | ||||
|     } | ||||
| } | ||||
|  | @ -29,7 +29,7 @@ import net.fabricmc.loom.configuration.providers.mappings.intermediary.Intermedi | |||
| class IntermediaryMappingLayerTest extends LayeredMappingsSpecification { | ||||
|     def "Read intermediary mappings" () { | ||||
|         setup: | ||||
|             mockMappingsProvider.intermediaryTinyFile() >> extractFileFromZip(downloadFile(INTERMEDIARY_1_17_URL, "intermediary.jar"), "mappings/mappings.tiny") | ||||
|             intermediaryUrl = INTERMEDIARY_1_17_URL | ||||
|         when: | ||||
|             def mappings = getSingleMapping(new IntermediaryMappingsSpec()) | ||||
|             def tiny = getTiny(mappings) | ||||
|  |  | |||
|  | @ -24,15 +24,14 @@ | |||
| 
 | ||||
| package net.fabricmc.loom.test.unit.layeredmappings | ||||
| 
 | ||||
| import groovy.transform.CompileStatic | ||||
| import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider | ||||
| import net.fabricmc.loom.configuration.providers.mappings.LayeredMappingSpec | ||||
| import net.fabricmc.loom.configuration.providers.mappings.LayeredMappingsProcessor | ||||
| import net.fabricmc.loom.api.mappings.layered.MappingContext | ||||
| import net.fabricmc.loom.api.mappings.layered.MappingLayer | ||||
| import net.fabricmc.loom.api.mappings.layered.MappingsNamespace | ||||
| import net.fabricmc.loom.configuration.providers.mappings.MappingsProvider | ||||
| import net.fabricmc.loom.api.mappings.layered.spec.MappingsSpec | ||||
| import net.fabricmc.loom.configuration.providers.mappings.IntermediaryService | ||||
| import net.fabricmc.loom.configuration.providers.mappings.LayeredMappingSpec | ||||
| import net.fabricmc.loom.configuration.providers.mappings.LayeredMappingsProcessor | ||||
| import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider | ||||
| import net.fabricmc.mappingio.adapter.MappingDstNsReorder | ||||
| import net.fabricmc.mappingio.adapter.MappingSourceNsSwitch | ||||
| import net.fabricmc.mappingio.format.Tiny2Writer | ||||
|  | @ -42,13 +41,13 @@ import org.gradle.api.logging.Logger | |||
| import spock.lang.Specification | ||||
| 
 | ||||
| import java.nio.file.Path | ||||
| import java.util.function.Supplier | ||||
| import java.util.zip.ZipFile | ||||
| 
 | ||||
| abstract class LayeredMappingsSpecification extends Specification implements LayeredMappingsTestConstants { | ||||
|     Logger mockLogger = Mock(Logger) | ||||
|     MappingsProvider mockMappingsProvider = Mock(MappingsProvider) | ||||
|     MinecraftProvider mockMinecraftProvider = Mock(MinecraftProvider) | ||||
| 
 | ||||
|     String intermediaryUrl | ||||
|     MappingContext mappingContext = new TestMappingContext() | ||||
| 
 | ||||
|     File tempDir = File.createTempDir() | ||||
|  | @ -102,7 +101,12 @@ abstract class LayeredMappingsSpecification extends Specification implements Lay | |||
|         return reorderedMappings | ||||
|     } | ||||
| 
 | ||||
|     @CompileStatic | ||||
|     def setup() { | ||||
|         mockMinecraftProvider.file(_) >> { args -> | ||||
|             return new File(tempDir, args[0]) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     class TestMappingContext implements MappingContext { | ||||
|         @Override | ||||
|         Path resolveDependency(Dependency dependency) { | ||||
|  | @ -116,8 +120,10 @@ abstract class LayeredMappingsSpecification extends Specification implements Lay | |||
|         } | ||||
| 
 | ||||
|         @Override | ||||
|         MappingsProvider mappingsProvider() { | ||||
|             return mockMappingsProvider | ||||
|         Supplier<MemoryMappingTree> intermediaryTree() { | ||||
|             return { | ||||
|                 IntermediaryService.create(intermediaryUrl, minecraftProvider()).memoryMappingTree | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         @Override | ||||
|  |  | |||
|  | @ -30,7 +30,7 @@ import net.fabricmc.loom.configuration.providers.mappings.mojmap.MojangMappingsS | |||
| class MojangMappingLayerTest extends LayeredMappingsSpecification { | ||||
|     def "Read mojang mappings with synthetic field names" () { | ||||
|         setup: | ||||
|             mockMappingsProvider.intermediaryTinyFile() >> extractFileFromZip(downloadFile(INTERMEDIARY_1_17_URL, "intermediary.jar"), "mappings/mappings.tiny") | ||||
|             intermediaryUrl = INTERMEDIARY_1_17_URL | ||||
|             mockMinecraftProvider.getVersionInfo() >> VERSION_META_1_17 | ||||
|         when: | ||||
|             def mappings = getLayeredMappings( | ||||
|  | @ -50,7 +50,7 @@ class MojangMappingLayerTest extends LayeredMappingsSpecification { | |||
| 
 | ||||
|     def "Read mojang mappings without synthetic field names" () { | ||||
|         setup: | ||||
|             mockMappingsProvider.intermediaryTinyFile() >> extractFileFromZip(downloadFile(INTERMEDIARY_1_17_URL, "intermediary.jar"), "mappings/mappings.tiny") | ||||
|         intermediaryUrl = INTERMEDIARY_1_17_URL | ||||
|             mockMinecraftProvider.getVersionInfo() >> VERSION_META_1_17 | ||||
|         when: | ||||
|             def mappings = getLayeredMappings( | ||||
|  |  | |||
|  | @ -32,7 +32,7 @@ import net.fabricmc.loom.configuration.providers.mappings.parchment.ParchmentMap | |||
| class ParchmentMappingLayerTest extends LayeredMappingsSpecification { | ||||
|     def "Read parchment mappings" () { | ||||
|         setup: | ||||
|             mockMappingsProvider.intermediaryTinyFile() >> extractFileFromZip(downloadFile(INTERMEDIARY_1_16_5_URL, "intermediary.jar"), "mappings/mappings.tiny") | ||||
|             intermediaryUrl = INTERMEDIARY_1_16_5_URL | ||||
|             mockMinecraftProvider.getVersionInfo() >> VERSION_META_1_16_5 | ||||
|         when: | ||||
|             withMavenFile(PARCHMENT_NOTATION, downloadFile(PARCHMENT_URL, "parchment.zip")) | ||||
|  | @ -55,7 +55,7 @@ class ParchmentMappingLayerTest extends LayeredMappingsSpecification { | |||
| 
 | ||||
|     def "Read parchment mappings remove prefix" () { | ||||
|         setup: | ||||
|             mockMappingsProvider.intermediaryTinyFile() >> extractFileFromZip(downloadFile(INTERMEDIARY_1_16_5_URL, "intermediary.jar"), "mappings/mappings.tiny") | ||||
|             intermediaryUrl = INTERMEDIARY_1_16_5_URL | ||||
|             mockMinecraftProvider.getVersionInfo() >> VERSION_META_1_16_5 | ||||
|         when: | ||||
|             withMavenFile(PARCHMENT_NOTATION, downloadFile(PARCHMENT_URL, "parchment.zip")) | ||||
|  |  | |||
|  | @ -65,6 +65,10 @@ trait GradleProjectTestTrait { | |||
|             String repo  = options.repo | ||||
|             String commit = options.commit | ||||
| 
 | ||||
|             if (options.allowExistingRepo && projectDir.listFiles()?.length > 0) { | ||||
|                 return | ||||
|             } | ||||
| 
 | ||||
|             exec(projectDir, "git", "clone", repo, ".") | ||||
|             exec(projectDir, "git", "checkout", commit) | ||||
| 
 | ||||
|  | @ -85,6 +89,7 @@ trait GradleProjectTestTrait { | |||
|     } | ||||
| 
 | ||||
|     private void exec(File projectDir, String... args) { | ||||
|         projectDir.mkdirs() | ||||
|         def process = args.execute([], projectDir) | ||||
|         process.consumeProcessOutput(System.out, System.err) | ||||
| 
 | ||||
|  |  | |||
|  | @ -90,9 +90,8 @@ shadowJar { | |||
| } | ||||
| 
 | ||||
| remapJar { | ||||
| 	dependsOn(shadowJar) | ||||
| 	archiveClassifier.set("universal") | ||||
| 	input.fileValue(tasks["shadowJar"].outputs.files.singleFile) | ||||
| 	inputFile = shadowJar.archiveFile | ||||
| } | ||||
| 
 | ||||
| jar { | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue