1616
1717package org .springframework .boot .build .bom ;
1818
19+ import java .io .File ;
20+ import java .io .IOException ;
21+ import java .io .InputStreamReader ;
22+ import java .nio .charset .StandardCharsets ;
1923import java .util .ArrayList ;
2024import java .util .HashMap ;
2125import java .util .LinkedHashMap ;
2226import java .util .List ;
2327import java .util .Map ;
2428import java .util .stream .Collectors ;
2529
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+
2638import groovy .lang .Closure ;
2739import groovy .lang .GroovyObjectSupport ;
2840import org .apache .maven .artifact .versioning .InvalidVersionSpecificationException ;
2941import org .apache .maven .artifact .versioning .VersionRange ;
42+ import org .gradle .api .Action ;
43+ import org .gradle .api .GradleException ;
3044import org .gradle .api .InvalidUserCodeException ;
3145import org .gradle .api .InvalidUserDataException ;
46+ import org .gradle .api .Project ;
47+ import org .gradle .api .Task ;
48+ import org .gradle .api .artifacts .Configuration ;
3249import org .gradle .api .artifacts .dsl .DependencyHandler ;
3350import 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 ;
3454import org .gradle .util .ConfigureUtil ;
55+ import org .w3c .dom .Document ;
56+ import org .w3c .dom .NodeList ;
3557
58+ import org .springframework .boot .build .DeployedPlugin ;
3659import org .springframework .boot .build .bom .Library .Exclusion ;
3760import org .springframework .boot .build .bom .Library .Group ;
3861import org .springframework .boot .build .bom .Library .Module ;
3962import org .springframework .boot .build .bom .Library .ProhibitedVersion ;
4063import org .springframework .boot .build .bom .bomr .version .DependencyVersion ;
64+ import org .springframework .boot .build .mavenplugin .MavenExec ;
65+ import org .springframework .util .FileCopyUtils ;
4166
4267/**
4368 * DSL extensions for {@link BomPlugin}.
@@ -56,8 +81,11 @@ public class BomExtension {
5681
5782 private final DependencyHandler dependencyHandler ;
5883
59- public BomExtension (DependencyHandler dependencyHandler ) {
84+ private final Project project ;
85+
86+ public BomExtension (DependencyHandler dependencyHandler , Project project ) {
6087 this .dependencyHandler = dependencyHandler ;
88+ this .project = project ;
6189 }
6290
6391 public List <Library > getLibraries () {
@@ -80,6 +108,43 @@ public void library(String name, String version, Closure<?> closure) {
80108 libraryHandler .prohibitedVersions ));
81109 }
82110
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+
83148 private String createDependencyNotation (String groupId , String artifactId , DependencyVersion version ) {
84149 return groupId + ":" + artifactId + ":" + version ;
85150 }
@@ -298,4 +363,38 @@ public List<String> getIssueLabels() {
298363
299364 }
300365
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+
301400}
0 commit comments