16
16
17
17
package org .springframework .boot .build .bom ;
18
18
19
+ import java .io .File ;
20
+ import java .io .IOException ;
21
+ import java .io .InputStreamReader ;
22
+ import java .nio .charset .StandardCharsets ;
19
23
import java .util .ArrayList ;
20
24
import java .util .HashMap ;
21
25
import java .util .LinkedHashMap ;
22
26
import java .util .List ;
23
27
import java .util .Map ;
24
28
import java .util .stream .Collectors ;
25
29
30
+ import javax .xml .parsers .DocumentBuilderFactory ;
31
+ import javax .xml .transform .TransformerFactory ;
32
+ import javax .xml .transform .dom .DOMSource ;
33
+ import javax .xml .transform .stream .StreamResult ;
34
+ import javax .xml .xpath .XPath ;
35
+ import javax .xml .xpath .XPathConstants ;
36
+ import javax .xml .xpath .XPathFactory ;
37
+
26
38
import groovy .lang .Closure ;
27
39
import groovy .lang .GroovyObjectSupport ;
28
40
import org .apache .maven .artifact .versioning .InvalidVersionSpecificationException ;
29
41
import org .apache .maven .artifact .versioning .VersionRange ;
42
+ import org .gradle .api .Action ;
43
+ import org .gradle .api .GradleException ;
30
44
import org .gradle .api .InvalidUserCodeException ;
31
45
import org .gradle .api .InvalidUserDataException ;
46
+ import org .gradle .api .Project ;
47
+ import org .gradle .api .Task ;
48
+ import org .gradle .api .artifacts .Configuration ;
32
49
import org .gradle .api .artifacts .dsl .DependencyHandler ;
33
50
import org .gradle .api .plugins .JavaPlatformPlugin ;
51
+ import org .gradle .api .publish .maven .tasks .GenerateMavenPom ;
52
+ import org .gradle .api .tasks .Sync ;
53
+ import org .gradle .api .tasks .TaskExecutionException ;
34
54
import org .gradle .util .ConfigureUtil ;
55
+ import org .w3c .dom .Document ;
56
+ import org .w3c .dom .NodeList ;
35
57
58
+ import org .springframework .boot .build .DeployedPlugin ;
36
59
import org .springframework .boot .build .bom .Library .Exclusion ;
37
60
import org .springframework .boot .build .bom .Library .Group ;
38
61
import org .springframework .boot .build .bom .Library .Module ;
39
62
import org .springframework .boot .build .bom .Library .ProhibitedVersion ;
40
63
import org .springframework .boot .build .bom .bomr .version .DependencyVersion ;
64
+ import org .springframework .boot .build .mavenplugin .MavenExec ;
65
+ import org .springframework .util .FileCopyUtils ;
41
66
42
67
/**
43
68
* DSL extensions for {@link BomPlugin}.
@@ -56,8 +81,11 @@ public class BomExtension {
56
81
57
82
private final DependencyHandler dependencyHandler ;
58
83
59
- public BomExtension (DependencyHandler dependencyHandler ) {
84
+ private final Project project ;
85
+
86
+ public BomExtension (DependencyHandler dependencyHandler , Project project ) {
60
87
this .dependencyHandler = dependencyHandler ;
88
+ this .project = project ;
61
89
}
62
90
63
91
public List <Library > getLibraries () {
@@ -80,6 +108,43 @@ public void library(String name, String version, Closure<?> closure) {
80
108
libraryHandler .prohibitedVersions ));
81
109
}
82
110
111
+ public void effectiveBomArtifact () {
112
+ Configuration effectiveBomConfiguration = this .project .getConfigurations ().create ("effectiveBom" );
113
+ this .project .getTasks ().matching ((task ) -> task .getName ().equals (DeployedPlugin .GENERATE_POM_TASK_NAME ))
114
+ .all ((task ) -> {
115
+ Sync syncBom = this .project .getTasks ().create ("syncBom" , Sync .class );
116
+ syncBom .dependsOn (task );
117
+ File generatedBomDir = new File (this .project .getBuildDir (), "generated/bom" );
118
+ syncBom .setDestinationDir (generatedBomDir );
119
+ syncBom .from (((GenerateMavenPom ) task ).getDestination (), (pom ) -> pom .rename ((name ) -> "pom.xml" ));
120
+ try {
121
+ String settingsXmlContent = FileCopyUtils
122
+ .copyToString (new InputStreamReader (
123
+ getClass ().getClassLoader ().getResourceAsStream ("effective-bom-settings.xml" ),
124
+ StandardCharsets .UTF_8 ))
125
+ .replace ("localRepositoryPath" ,
126
+ new File (this .project .getBuildDir (), "local-m2-repository" ).getAbsolutePath ());
127
+ syncBom .from (this .project .getResources ().getText ().fromString (settingsXmlContent ),
128
+ (settingsXml ) -> settingsXml .rename ((name ) -> "settings.xml" ));
129
+ }
130
+ catch (IOException ex ) {
131
+ throw new GradleException ("Failed to prepare settings.xml" , ex );
132
+ }
133
+ MavenExec generateEffectiveBom = this .project .getTasks ().create ("generateEffectiveBom" ,
134
+ MavenExec .class );
135
+ generateEffectiveBom .setProjectDir (generatedBomDir );
136
+ File effectiveBom = new File (this .project .getBuildDir (),
137
+ "generated/effective-bom/" + this .project .getName () + "-effective-bom.xml" );
138
+ generateEffectiveBom .args ("--settings" , "settings.xml" , "help:effective-pom" ,
139
+ "-Doutput=" + effectiveBom );
140
+ generateEffectiveBom .dependsOn (syncBom );
141
+ generateEffectiveBom .getOutputs ().file (effectiveBom );
142
+ generateEffectiveBom .doLast (new StripUnrepeatableOutputAction (effectiveBom ));
143
+ this .project .getArtifacts ().add (effectiveBomConfiguration .getName (), effectiveBom ,
144
+ (artifact ) -> artifact .builtBy (generateEffectiveBom ));
145
+ });
146
+ }
147
+
83
148
private String createDependencyNotation (String groupId , String artifactId , DependencyVersion version ) {
84
149
return groupId + ":" + artifactId + ":" + version ;
85
150
}
@@ -298,4 +363,38 @@ public List<String> getIssueLabels() {
298
363
299
364
}
300
365
366
+ private static final class StripUnrepeatableOutputAction implements Action <Task > {
367
+
368
+ private final File effectiveBom ;
369
+
370
+ private StripUnrepeatableOutputAction (File xmlFile ) {
371
+ this .effectiveBom = xmlFile ;
372
+ }
373
+
374
+ @ Override
375
+ public void execute (Task task ) {
376
+ try {
377
+ Document document = DocumentBuilderFactory .newInstance ().newDocumentBuilder ().parse (this .effectiveBom );
378
+ XPath xpath = XPathFactory .newInstance ().newXPath ();
379
+ NodeList comments = (NodeList ) xpath .evaluate ("//comment()" , document , XPathConstants .NODESET );
380
+ for (int i = 0 ; i < comments .getLength (); i ++) {
381
+ org .w3c .dom .Node comment = comments .item (i );
382
+ comment .getParentNode ().removeChild (comment );
383
+ }
384
+ org .w3c .dom .Node build = (org .w3c .dom .Node ) xpath .evaluate ("/project/build" , document ,
385
+ XPathConstants .NODE );
386
+ build .getParentNode ().removeChild (build );
387
+ org .w3c .dom .Node reporting = (org .w3c .dom .Node ) xpath .evaluate ("/project/reporting" , document ,
388
+ XPathConstants .NODE );
389
+ reporting .getParentNode ().removeChild (reporting );
390
+ TransformerFactory .newInstance ().newTransformer ().transform (new DOMSource (document ),
391
+ new StreamResult (this .effectiveBom ));
392
+ }
393
+ catch (Exception ex ) {
394
+ throw new TaskExecutionException (task , ex );
395
+ }
396
+ }
397
+
398
+ }
399
+
301
400
}
0 commit comments