commit
						7088d0e04a
					
				|  | @ -22,7 +22,6 @@ import java.nio.file.Files; | |||
| import java.nio.file.StandardOpenOption; | ||||
| import java.util.List; | ||||
| import java.util.stream.Collectors; | ||||
| import java.util.stream.Stream; | ||||
| 
 | ||||
| import com.tngtech.archunit.base.DescribedPredicate; | ||||
| import com.tngtech.archunit.core.domain.JavaClass; | ||||
|  | @ -45,9 +44,12 @@ import org.gradle.api.Task; | |||
| import org.gradle.api.file.DirectoryProperty; | ||||
| import org.gradle.api.file.FileCollection; | ||||
| import org.gradle.api.file.FileTree; | ||||
| import org.gradle.api.provider.ListProperty; | ||||
| import org.gradle.api.tasks.IgnoreEmptyDirectories; | ||||
| import org.gradle.api.tasks.Input; | ||||
| import org.gradle.api.tasks.InputFiles; | ||||
| import org.gradle.api.tasks.Internal; | ||||
| import org.gradle.api.tasks.Optional; | ||||
| import org.gradle.api.tasks.OutputDirectory; | ||||
| import org.gradle.api.tasks.PathSensitive; | ||||
| import org.gradle.api.tasks.PathSensitivity; | ||||
|  | @ -65,17 +67,20 @@ public abstract class ArchitectureCheck extends DefaultTask { | |||
| 
 | ||||
| 	public ArchitectureCheck() { | ||||
| 		getOutputDirectory().convention(getProject().getLayout().getBuildDirectory().dir(getName())); | ||||
| 		getRules().addAll(allPackagesShouldBeFreeOfTangles(), | ||||
| 				allBeanPostProcessorBeanMethodsShouldBeStaticAndHaveParametersThatWillNotCausePrematureInitialization(), | ||||
| 				allBeanFactoryPostProcessorBeanMethodsShouldBeStaticAndHaveNoParameters(), | ||||
| 				noClassesShouldCallStepVerifierStepVerifyComplete(), | ||||
| 				noClassesShouldConfigureDefaultStepVerifierTimeout(), noClassesShouldCallCollectorsToList()); | ||||
| 		getRuleDescriptions().set(getRules().map((rules) -> rules.stream().map(ArchRule::getDescription).toList())); | ||||
| 	} | ||||
| 
 | ||||
| 	@TaskAction | ||||
| 	void checkArchitecture() throws IOException { | ||||
| 		JavaClasses javaClasses = new ClassFileImporter() | ||||
| 			.importPaths(this.classes.getFiles().stream().map(File::toPath).toList()); | ||||
| 		List<EvaluationResult> violations = Stream.of(allPackagesShouldBeFreeOfTangles(), | ||||
| 				allBeanPostProcessorBeanMethodsShouldBeStaticAndHaveParametersThatWillNotCausePrematureInitialization(), | ||||
| 				allBeanFactoryPostProcessorBeanMethodsShouldBeStaticAndHaveNoParameters(), | ||||
| 				noClassesShouldCallStepVerifierStepVerifyComplete(), | ||||
| 				noClassesShouldConfigureDefaultStepVerifierTimeout(), noClassesShouldCallCollectorsToList()) | ||||
| 		List<EvaluationResult> violations = getRules().get() | ||||
| 			.stream() | ||||
| 			.map((rule) -> rule.evaluate(javaClasses)) | ||||
| 			.filter(EvaluationResult::hasViolation) | ||||
| 			.toList(); | ||||
|  | @ -202,7 +207,20 @@ public abstract class ArchitectureCheck extends DefaultTask { | |||
| 		return this.classes.getAsFileTree(); | ||||
| 	} | ||||
| 
 | ||||
| 	@Optional | ||||
| 	@InputFiles | ||||
| 	@PathSensitive(PathSensitivity.RELATIVE) | ||||
| 	public abstract DirectoryProperty getResourcesDirectory(); | ||||
| 
 | ||||
| 	@OutputDirectory | ||||
| 	public abstract DirectoryProperty getOutputDirectory(); | ||||
| 
 | ||||
| 	@Internal | ||||
| 	public abstract ListProperty<ArchRule> getRules(); | ||||
| 
 | ||||
| 	@Input | ||||
| 	// The rules themselves can't be an input as they aren't serializable so we use their | ||||
| 	// descriptions instead | ||||
| 	abstract ListProperty<String> getRuleDescriptions(); | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -50,6 +50,7 @@ public class ArchitecturePlugin implements Plugin<Project> { | |||
| 				.register("checkArchitecture" + StringUtils.capitalize(sourceSet.getName()), ArchitectureCheck.class, | ||||
| 						(task) -> { | ||||
| 							task.setClasses(sourceSet.getOutput().getClassesDirs()); | ||||
| 							task.getResourcesDirectory().set(sourceSet.getOutput().getResourcesDir()); | ||||
| 							task.setDescription("Checks the architecture of the classes of the " + sourceSet.getName() | ||||
| 									+ " source set."); | ||||
| 							task.setGroup(LifecycleBasePlugin.VERIFICATION_GROUP); | ||||
|  |  | |||
|  | @ -17,27 +17,48 @@ | |||
| package org.springframework.boot.build.autoconfigure; | ||||
| 
 | ||||
| import java.io.File; | ||||
| import java.io.IOException; | ||||
| import java.nio.file.Files; | ||||
| import java.nio.file.Path; | ||||
| import java.util.Collections; | ||||
| import java.util.List; | ||||
| import java.util.concurrent.Callable; | ||||
| 
 | ||||
| import com.tngtech.archunit.core.domain.JavaClass; | ||||
| import com.tngtech.archunit.lang.ArchCondition; | ||||
| import com.tngtech.archunit.lang.ArchRule; | ||||
| import com.tngtech.archunit.lang.ConditionEvents; | ||||
| import com.tngtech.archunit.lang.SimpleConditionEvent; | ||||
| import com.tngtech.archunit.lang.syntax.ArchRuleDefinition; | ||||
| import org.gradle.api.Plugin; | ||||
| import org.gradle.api.Project; | ||||
| import org.gradle.api.artifacts.Configuration; | ||||
| import org.gradle.api.plugins.JavaPlugin; | ||||
| import org.gradle.api.plugins.JavaPluginExtension; | ||||
| import org.gradle.api.provider.Provider; | ||||
| import org.gradle.api.tasks.PathSensitivity; | ||||
| import org.gradle.api.tasks.SourceSet; | ||||
| 
 | ||||
| import org.springframework.boot.build.DeployedPlugin; | ||||
| import org.springframework.boot.build.architecture.ArchitectureCheck; | ||||
| import org.springframework.boot.build.architecture.ArchitecturePlugin; | ||||
| 
 | ||||
| /** | ||||
|  * {@link Plugin} for projects that define auto-configuration. When applied, the plugin | ||||
|  * applies the {@link DeployedPlugin}. Additionally, it reacts to the presence of the | ||||
|  * {@link JavaPlugin} by: | ||||
|  * applies the {@link DeployedPlugin}. Additionally, when the {@link JavaPlugin} is | ||||
|  * applied it: | ||||
|  * | ||||
|  * <ul> | ||||
|  * <li>Adding a dependency on the auto-configuration annotation processor. | ||||
|  * <li>Defining a task that produces metadata describing the auto-configuration. The | ||||
|  * metadata is made available as an artifact in the | ||||
|  * <li>Adds a dependency on the auto-configuration annotation processor. | ||||
|  * <li>Defines a task that produces metadata describing the auto-configuration. The | ||||
|  * metadata is made available as an artifact in the {@code autoConfigurationMetadata} | ||||
|  * configuration. | ||||
|  * <li>Reacts to the {@link ArchitecturePlugin} being applied and: | ||||
|  * <ul> | ||||
|  * <li>Adds a rule to the {@code checkArchitectureMain} task to verify that all | ||||
|  * {@code AutoConfiguration} classes are listed in the {@code AutoConfiguration.imports} | ||||
|  * file. | ||||
|  * </ul> | ||||
|  * </ul> | ||||
|  * | ||||
|  * @author Andy Wilkinson | ||||
|  | @ -50,6 +71,8 @@ public class AutoConfigurationPlugin implements Plugin<Project> { | |||
| 	 */ | ||||
| 	public static final String AUTO_CONFIGURATION_METADATA_CONFIGURATION_NAME = "autoConfigurationMetadata"; | ||||
| 
 | ||||
| 	private static final String AUTO_CONFIGURATION_IMPORTS_PATH = "META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports"; | ||||
| 
 | ||||
| 	@Override | ||||
| 	public void apply(Project project) { | ||||
| 		project.getPlugins().apply(DeployedPlugin.class); | ||||
|  | @ -77,7 +100,62 @@ public class AutoConfigurationPlugin implements Plugin<Project> { | |||
| 							project.provider((Callable<File>) task::getOutputFile), | ||||
| 							(artifact) -> artifact.builtBy(task)); | ||||
| 			}); | ||||
| 			project.getPlugins().withType(ArchitecturePlugin.class, (architecturePlugin) -> { | ||||
| 				project.getTasks().named("checkArchitectureMain", ArchitectureCheck.class).configure((task) -> { | ||||
| 					SourceSet main = project.getExtensions() | ||||
| 						.getByType(JavaPluginExtension.class) | ||||
| 						.getSourceSets() | ||||
| 						.getByName(SourceSet.MAIN_SOURCE_SET_NAME); | ||||
| 					File resourcesDirectory = main.getOutput().getResourcesDir(); | ||||
| 					task.dependsOn(main.getProcessResourcesTaskName()); | ||||
| 					task.getInputs().files(resourcesDirectory).optional().withPathSensitivity(PathSensitivity.RELATIVE); | ||||
| 					task.getRules() | ||||
| 						.add(allClassesAnnotatedWithAutoConfigurationShouldBeListedInAutoConfigurationImports( | ||||
| 								autoConfigurationImports(project, resourcesDirectory))); | ||||
| 				}); | ||||
| 			}); | ||||
| 		}); | ||||
| 	} | ||||
| 
 | ||||
| 	private ArchRule allClassesAnnotatedWithAutoConfigurationShouldBeListedInAutoConfigurationImports( | ||||
| 			Provider<AutoConfigurationImports> imports) { | ||||
| 		return ArchRuleDefinition.classes() | ||||
| 			.that() | ||||
| 			.areAnnotatedWith("org.springframework.boot.autoconfigure.AutoConfiguration") | ||||
| 			.should(beListedInAutoConfigurationImports(imports)) | ||||
| 			.allowEmptyShould(true); | ||||
| 	} | ||||
| 
 | ||||
| 	private ArchCondition<JavaClass> beListedInAutoConfigurationImports(Provider<AutoConfigurationImports> imports) { | ||||
| 		return new ArchCondition<>("be listed in " + AUTO_CONFIGURATION_IMPORTS_PATH) { | ||||
| 
 | ||||
| 			@Override | ||||
| 			public void check(JavaClass item, ConditionEvents events) { | ||||
| 				AutoConfigurationImports autoConfigurationImports = imports.get(); | ||||
| 				if (!autoConfigurationImports.imports.contains(item.getName())) { | ||||
| 					events.add(SimpleConditionEvent.violated(item, | ||||
| 							item.getName() + " was not listed in " + autoConfigurationImports.importsFile)); | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 		}; | ||||
| 	} | ||||
| 
 | ||||
| 	private Provider<AutoConfigurationImports> autoConfigurationImports(Project project, File resourcesDirectory) { | ||||
| 		Path importsFile = new File(resourcesDirectory, AUTO_CONFIGURATION_IMPORTS_PATH).toPath(); | ||||
| 		return project.provider(() -> { | ||||
| 			try { | ||||
| 				return new AutoConfigurationImports(project.getProjectDir().toPath().relativize(importsFile), | ||||
| 						Files.readAllLines(importsFile)); | ||||
| 			} | ||||
| 			catch (IOException ex) { | ||||
| 				throw new RuntimeException("Failed to read AutoConfiguration.imports", ex); | ||||
| 			} | ||||
| 		}); | ||||
| 	} | ||||
| 
 | ||||
| 	private static record AutoConfigurationImports(Path importsFile, List<String> imports) { | ||||
| 
 | ||||
| 	} | ||||
| 
 | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue