Add AccessWidenerValidator (#518)
* Add AccessWidenerValidator * Move to task * Review feedback
This commit is contained in:
		
							parent
							
								
									0248bacc16
								
							
						
					
					
						commit
						a10307464e
					
				
					 5 changed files with 138 additions and 2 deletions
				
			
		|  | @ -39,13 +39,13 @@ import org.gradle.api.artifacts.ExternalModuleDependency; | ||||||
| import org.gradle.api.artifacts.repositories.MavenArtifactRepository; | import org.gradle.api.artifacts.repositories.MavenArtifactRepository; | ||||||
| 
 | 
 | ||||||
| import net.fabricmc.loom.LoomGradleExtension; | import net.fabricmc.loom.LoomGradleExtension; | ||||||
|  | import net.fabricmc.loom.LoomRepositoryPlugin; | ||||||
| import net.fabricmc.loom.build.ModCompileRemapper; | import net.fabricmc.loom.build.ModCompileRemapper; | ||||||
| import net.fabricmc.loom.configuration.DependencyProvider.DependencyInfo; | import net.fabricmc.loom.configuration.DependencyProvider.DependencyInfo; | ||||||
| import net.fabricmc.loom.configuration.mods.ModProcessor; | import net.fabricmc.loom.configuration.mods.ModProcessor; | ||||||
| import net.fabricmc.loom.configuration.providers.mappings.MappingsProviderImpl; | import net.fabricmc.loom.configuration.providers.mappings.MappingsProviderImpl; | ||||||
| import net.fabricmc.loom.util.Constants; | import net.fabricmc.loom.util.Constants; | ||||||
| import net.fabricmc.loom.util.SourceRemapper; | import net.fabricmc.loom.util.SourceRemapper; | ||||||
| import net.fabricmc.loom.LoomRepositoryPlugin; |  | ||||||
| 
 | 
 | ||||||
| public class LoomDependencyManager { | public class LoomDependencyManager { | ||||||
| 	private static class ProviderList { | 	private static class ProviderList { | ||||||
|  |  | ||||||
|  | @ -57,6 +57,13 @@ public final class LoomTasks { | ||||||
| 		tasks.register("downloadAssets", DownloadAssetsTask.class, t -> t.setDescription("Downloads required assets for Fabric.")); | 		tasks.register("downloadAssets", DownloadAssetsTask.class, t -> t.setDescription("Downloads required assets for Fabric.")); | ||||||
| 		tasks.register("remapSourcesJar", RemapSourcesJarTask.class, t -> t.setDescription("Remaps the project sources jar to intermediary names.")); | 		tasks.register("remapSourcesJar", RemapSourcesJarTask.class, t -> t.setDescription("Remaps the project sources jar to intermediary names.")); | ||||||
| 
 | 
 | ||||||
|  | 		tasks.getByName("check").dependsOn( | ||||||
|  | 				tasks.register("validateAccessWidener", ValidateAccessWidenerTask.class, t -> { | ||||||
|  | 					t.setDescription("Validate all the rules in the access widener against the Minecraft jar"); | ||||||
|  | 					t.setGroup("verification"); | ||||||
|  | 				}) | ||||||
|  | 		); | ||||||
|  | 
 | ||||||
| 		registerIDETasks(tasks); | 		registerIDETasks(tasks); | ||||||
| 		registerRunTasks(tasks, project); | 		registerRunTasks(tasks, project); | ||||||
| 		registerDecompileTasks(tasks, project); | 		registerDecompileTasks(tasks, project); | ||||||
|  |  | ||||||
|  | @ -0,0 +1,109 @@ | ||||||
|  | /* | ||||||
|  |  * This file is part of fabric-loom, licensed under the MIT License (MIT). | ||||||
|  |  * | ||||||
|  |  * Copyright (c) 2021 FabricMC | ||||||
|  |  * | ||||||
|  |  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||||
|  |  * of this software and associated documentation files (the "Software"), to deal | ||||||
|  |  * in the Software without restriction, including without limitation the rights | ||||||
|  |  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||||
|  |  * copies of the Software, and to permit persons to whom the Software is | ||||||
|  |  * furnished to do so, subject to the following conditions: | ||||||
|  |  * | ||||||
|  |  * The above copyright notice and this permission notice shall be included in all | ||||||
|  |  * copies or substantial portions of the Software. | ||||||
|  |  * | ||||||
|  |  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||||
|  |  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||||
|  |  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||||
|  |  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||||
|  |  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||||
|  |  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||||
|  |  * SOFTWARE. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package net.fabricmc.loom.task; | ||||||
|  | 
 | ||||||
|  | import java.io.BufferedReader; | ||||||
|  | import java.io.IOException; | ||||||
|  | import java.io.UncheckedIOException; | ||||||
|  | import java.nio.charset.StandardCharsets; | ||||||
|  | import java.nio.file.Files; | ||||||
|  | 
 | ||||||
|  | import javax.inject.Inject; | ||||||
|  | 
 | ||||||
|  | import org.gradle.api.DefaultTask; | ||||||
|  | import org.gradle.api.file.RegularFileProperty; | ||||||
|  | import org.gradle.api.tasks.InputFile; | ||||||
|  | import org.gradle.api.tasks.SkipWhenEmpty; | ||||||
|  | import org.gradle.api.tasks.TaskAction; | ||||||
|  | 
 | ||||||
|  | import net.fabricmc.accesswidener.AccessWidenerFormatException; | ||||||
|  | import net.fabricmc.accesswidener.AccessWidenerReader; | ||||||
|  | import net.fabricmc.accesswidener.AccessWidenerVisitor; | ||||||
|  | import net.fabricmc.loom.LoomGradleExtension; | ||||||
|  | import net.fabricmc.tinyremapper.TinyRemapper; | ||||||
|  | import net.fabricmc.tinyremapper.api.TrEnvironment; | ||||||
|  | 
 | ||||||
|  | public abstract class ValidateAccessWidenerTask extends DefaultTask { | ||||||
|  | 	@SkipWhenEmpty | ||||||
|  | 	@InputFile | ||||||
|  | 	public abstract RegularFileProperty getAccessWidener(); | ||||||
|  | 
 | ||||||
|  | 	@InputFile | ||||||
|  | 	public abstract RegularFileProperty getTargetJar(); | ||||||
|  | 
 | ||||||
|  | 	@Inject | ||||||
|  | 	public ValidateAccessWidenerTask() { | ||||||
|  | 		final LoomGradleExtension extension = LoomGradleExtension.get(getProject()); | ||||||
|  | 
 | ||||||
|  | 		getAccessWidener().convention(extension.getAccessWidenerPath()).finalizeValueOnRead(); | ||||||
|  | 		getTargetJar().convention(getProject().getObjects().fileProperty().fileValue(extension.getMinecraftMappedProvider().getMappedJar())).finalizeValueOnRead(); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@TaskAction | ||||||
|  | 	public void run() { | ||||||
|  | 		final TinyRemapper tinyRemapper = TinyRemapper.newRemapper().build(); | ||||||
|  | 		tinyRemapper.readClassPath(getTargetJar().get().getAsFile().toPath()); | ||||||
|  | 
 | ||||||
|  | 		final AccessWidenerValidator validator = new AccessWidenerValidator(tinyRemapper.getEnvironment()); | ||||||
|  | 		final AccessWidenerReader accessWidenerReader = new AccessWidenerReader(validator); | ||||||
|  | 
 | ||||||
|  | 		try (BufferedReader reader = Files.newBufferedReader(getAccessWidener().get().getAsFile().toPath(), StandardCharsets.UTF_8)) { | ||||||
|  | 			accessWidenerReader.read(reader, "named"); | ||||||
|  | 		} catch (AccessWidenerFormatException e) { | ||||||
|  | 			getProject().getLogger().error("Failed to validate access-widener file {} on line {}: {}", getAccessWidener().get().getAsFile().getName(), e.getLineNumber(), e.getMessage()); | ||||||
|  | 			throw e; | ||||||
|  | 		} catch (IOException e) { | ||||||
|  | 			throw new UncheckedIOException("Failed to read access widener", e); | ||||||
|  | 		} finally { | ||||||
|  | 			tinyRemapper.finish(); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/** | ||||||
|  | 	 * Validates that all entries in an access-widner file relate to a class/method/field in the mc jar. | ||||||
|  | 	 */ | ||||||
|  | 	private static record AccessWidenerValidator(TrEnvironment environment) implements AccessWidenerVisitor { | ||||||
|  | 		@Override | ||||||
|  | 		public void visitClass(String name, AccessWidenerReader.AccessType access, boolean transitive) { | ||||||
|  | 			if (environment().getClass(name) == null) { | ||||||
|  | 				throw new RuntimeException("Could not find class (%s)".formatted(name)); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		@Override | ||||||
|  | 		public void visitMethod(String owner, String name, String descriptor, AccessWidenerReader.AccessType access, boolean transitive) { | ||||||
|  | 			if (environment().getMethod(owner, name, descriptor) == null) { | ||||||
|  | 				throw new RuntimeException("Could not find method (%s%s) in class (%s)".formatted(name, descriptor, owner)); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		@Override | ||||||
|  | 		public void visitField(String owner, String name, String descriptor, AccessWidenerReader.AccessType access, boolean transitive) { | ||||||
|  | 			if (environment().getField(owner, name, descriptor) == null) { | ||||||
|  | 				throw new RuntimeException("Could not find field (%s%s) in class (%s)".formatted(name, descriptor, owner)); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | @ -65,4 +65,24 @@ class AccessWidenerTest extends Specification implements GradleProjectTestTrait | ||||||
| 		where: | 		where: | ||||||
| 			version << STANDARD_TEST_VERSIONS | 			version << STANDARD_TEST_VERSIONS | ||||||
| 	} | 	} | ||||||
|  | 
 | ||||||
|  | 	@Unroll | ||||||
|  | 	def "invalid (#awLine)"() { | ||||||
|  | 		setup: | ||||||
|  | 			def gradle = gradleProject(project: "accesswidener", version: version) | ||||||
|  | 			new File(gradle.projectDir, "src/main/resources/modid.accesswidener").append(awLine) | ||||||
|  | 			def errorPrefix = "Failed to validate access-widener file modid.accesswidener on line 10: java.lang.RuntimeException: " | ||||||
|  | 
 | ||||||
|  | 		when: | ||||||
|  | 			def result = gradle.run(task: "check", expectFailure: true) | ||||||
|  | 
 | ||||||
|  | 		then: | ||||||
|  | 			result.output.contains(errorPrefix + error) | ||||||
|  | 
 | ||||||
|  | 		where: | ||||||
|  | 			awLine 																					| error																									| version | ||||||
|  | 			'accessible\tclass\tnet/minecraft/DoesntExists'											| "Could not find class (net/minecraft/DoesntExists)"													| DEFAULT_GRADLE | ||||||
|  | 			'accessible\tfield\tnet/minecraft/screen/slot/Slot\tabc\tI'								| "Could not find field (abcI) in class (net/minecraft/screen/slot/Slot)"								| DEFAULT_GRADLE | ||||||
|  | 			'accessible\tmethod\tnet/minecraft/client/main/Main\tmain\t([Ljava/lang/NotAString;)V'	| "Could not find method (main([Ljava/lang/NotAString;)V) in class (net/minecraft/client/main/Main)"	| DEFAULT_GRADLE | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -156,7 +156,7 @@ trait GradleProjectTestTrait { | ||||||
| 
 | 
 | ||||||
|             runner.withArguments(args as String[]) |             runner.withArguments(args as String[]) | ||||||
| 
 | 
 | ||||||
|             return runner.build() |             return options.expectFailure ? runner.buildAndFail() : runner.build() | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         private GradleRunner getRunner() { |         private GradleRunner getRunner() { | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue