1616
1717package org .springframework .boot .build .autoconfigure ;
1818
19- import java .io .File ;
20- import java .io .IOException ;
21- import java .nio .file .Files ;
22- import java .nio .file .Path ;
2319import java .util .Collections ;
24- import java .util .List ;
20+ import java .util .Map ;
2521
26- import com .tngtech .archunit .core .domain .JavaClass ;
27- import com .tngtech .archunit .lang .ArchCondition ;
28- import com .tngtech .archunit .lang .ArchRule ;
29- import com .tngtech .archunit .lang .ConditionEvents ;
30- import com .tngtech .archunit .lang .SimpleConditionEvent ;
31- import com .tngtech .archunit .lang .syntax .ArchRuleDefinition ;
3222import org .gradle .api .Plugin ;
3323import org .gradle .api .Project ;
3424import org .gradle .api .artifacts .Configuration ;
3525import org .gradle .api .plugins .JavaPlugin ;
3626import org .gradle .api .plugins .JavaPluginExtension ;
37- import org .gradle .api .provider .Provider ;
38- import org .gradle .api .tasks .PathSensitivity ;
3927import org .gradle .api .tasks .SourceSet ;
28+ import org .gradle .api .tasks .TaskProvider ;
4029
4130import org .springframework .boot .build .DeployedPlugin ;
42- import org .springframework .boot .build .architecture .ArchitectureCheck ;
4331import org .springframework .boot .build .architecture .ArchitecturePlugin ;
32+ import org .springframework .boot .build .optional .OptionalDependenciesPlugin ;
4433
4534/**
4635 * {@link Plugin} for projects that define auto-configuration. When applied, the plugin
@@ -70,14 +59,16 @@ public class AutoConfigurationPlugin implements Plugin<Project> {
7059 */
7160 public static final String AUTO_CONFIGURATION_METADATA_CONFIGURATION_NAME = "autoConfigurationMetadata" ;
7261
73- private static final String AUTO_CONFIGURATION_IMPORTS_PATH = "META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports" ;
74-
7562 @ Override
7663 public void apply (Project project ) {
7764 project .getPlugins ().apply (DeployedPlugin .class );
7865 project .getPlugins ().withType (JavaPlugin .class , (javaPlugin ) -> {
7966 Configuration annotationProcessors = project .getConfigurations ()
8067 .getByName (JavaPlugin .ANNOTATION_PROCESSOR_CONFIGURATION_NAME );
68+ SourceSet main = project .getExtensions ()
69+ .getByType (JavaPluginExtension .class )
70+ .getSourceSets ()
71+ .getByName (SourceSet .MAIN_SOURCE_SET_NAME );
8172 annotationProcessors .getDependencies ()
8273 .add (project .getDependencies ()
8374 .project (Collections .singletonMap ("path" ,
@@ -87,10 +78,6 @@ public void apply(Project project) {
8778 .project (Collections .singletonMap ("path" ,
8879 ":spring-boot-project:spring-boot-tools:spring-boot-configuration-processor" )));
8980 project .getTasks ().register ("autoConfigurationMetadata" , AutoConfigurationMetadata .class , (task ) -> {
90- SourceSet main = project .getExtensions ()
91- .getByType (JavaPluginExtension .class )
92- .getSourceSets ()
93- .getByName (SourceSet .MAIN_SOURCE_SET_NAME );
9481 task .setSourceSet (main );
9582 task .dependsOn (main .getClassesTaskName ());
9683 task .getOutputFile ()
@@ -99,74 +86,37 @@ public void apply(Project project) {
9986 .add (AutoConfigurationPlugin .AUTO_CONFIGURATION_METADATA_CONFIGURATION_NAME , task .getOutputFile (),
10087 (artifact ) -> artifact .builtBy (task ));
10188 });
89+ project .getTasks ()
90+ .register ("checkAutoConfigurationImports" , CheckAutoConfigurationImports .class , (task ) -> {
91+ task .setSource (main .getResources ());
92+ task .setClasspath (main .getOutput ().getClassesDirs ());
93+ task .setDescription ("Checks the %s file of the main source set."
94+ .formatted (AutoConfigurationImportsTask .IMPORTS_FILE ));
95+ });
96+ Configuration requiredClasspath = project .getConfigurations ()
97+ .create ("autoConfigurationRequiredClasspath" )
98+ .extendsFrom (project .getConfigurations ().getByName (main .getImplementationConfigurationName ()),
99+ project .getConfigurations ().getByName (main .getRuntimeOnlyConfigurationName ()));
100+ requiredClasspath .getDependencies ()
101+ .add (project .getDependencies ()
102+ .project (Map .of ("path" , ":spring-boot-project:spring-boot-autoconfigure" )));
103+ TaskProvider <CheckAutoConfigurationClasses > checkAutoConfigurationClasses = project .getTasks ()
104+ .register ("checkAutoConfigurationClasses" , CheckAutoConfigurationClasses .class , (task ) -> {
105+ task .setSource (main .getResources ());
106+ task .setClasspath (main .getOutput ().getClassesDirs ());
107+ task .setRequiredDependencies (requiredClasspath );
108+ task .setDescription ("Checks the auto-configuration classes of the main source set." );
109+ });
102110 project .getPlugins ()
103- .withType (ArchitecturePlugin .class , (plugin ) -> configureArchitecturePluginTasks (project ));
104- });
105- }
106-
107- private void configureArchitecturePluginTasks (Project project ) {
108- project .getTasks ().configureEach ((task ) -> {
109- if ("checkArchitectureMain" .equals (task .getName ()) && task instanceof ArchitectureCheck architectureCheck ) {
110- configureCheckArchitectureMain (project , architectureCheck );
111- }
111+ .withType (OptionalDependenciesPlugin .class ,
112+ (plugin ) -> checkAutoConfigurationClasses .configure ((check ) -> {
113+ Configuration optionalClasspath = project .getConfigurations ()
114+ .create ("autoConfigurationOptionalClassPath" )
115+ .extendsFrom (project .getConfigurations ()
116+ .getByName (OptionalDependenciesPlugin .OPTIONAL_CONFIGURATION_NAME ));
117+ check .setOptionalDependencies (optionalClasspath );
118+ }));
112119 });
113120 }
114121
115- private void configureCheckArchitectureMain (Project project , ArchitectureCheck architectureCheck ) {
116- SourceSet main = project .getExtensions ()
117- .getByType (JavaPluginExtension .class )
118- .getSourceSets ()
119- .getByName (SourceSet .MAIN_SOURCE_SET_NAME );
120- File resourcesDirectory = main .getOutput ().getResourcesDir ();
121- architectureCheck .dependsOn (main .getProcessResourcesTaskName ());
122- architectureCheck .getInputs ()
123- .files (resourcesDirectory )
124- .optional ()
125- .withPathSensitivity (PathSensitivity .RELATIVE );
126- architectureCheck .getRules ()
127- .add (allClassesAnnotatedWithAutoConfigurationShouldBeListedInAutoConfigurationImports (
128- autoConfigurationImports (project , resourcesDirectory )));
129- }
130-
131- private ArchRule allClassesAnnotatedWithAutoConfigurationShouldBeListedInAutoConfigurationImports (
132- Provider <AutoConfigurationImports > imports ) {
133- return ArchRuleDefinition .classes ()
134- .that ()
135- .areAnnotatedWith ("org.springframework.boot.autoconfigure.AutoConfiguration" )
136- .should (beListedInAutoConfigurationImports (imports ))
137- .allowEmptyShould (true );
138- }
139-
140- private ArchCondition <JavaClass > beListedInAutoConfigurationImports (Provider <AutoConfigurationImports > imports ) {
141- return new ArchCondition <>("be listed in " + AUTO_CONFIGURATION_IMPORTS_PATH ) {
142-
143- @ Override
144- public void check (JavaClass item , ConditionEvents events ) {
145- AutoConfigurationImports autoConfigurationImports = imports .get ();
146- if (!autoConfigurationImports .imports .contains (item .getName ())) {
147- events .add (SimpleConditionEvent .violated (item ,
148- item .getName () + " was not listed in " + autoConfigurationImports .importsFile ));
149- }
150- }
151-
152- };
153- }
154-
155- private Provider <AutoConfigurationImports > autoConfigurationImports (Project project , File resourcesDirectory ) {
156- Path importsFile = new File (resourcesDirectory , AUTO_CONFIGURATION_IMPORTS_PATH ).toPath ();
157- return project .provider (() -> {
158- try {
159- return new AutoConfigurationImports (project .getProjectDir ().toPath ().relativize (importsFile ),
160- Files .readAllLines (importsFile ));
161- }
162- catch (IOException ex ) {
163- throw new RuntimeException ("Failed to read AutoConfiguration.imports" , ex );
164- }
165- });
166- }
167-
168- private record AutoConfigurationImports (Path importsFile , List <String > imports ) {
169-
170- }
171-
172122}
0 commit comments