View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.resolver.internal.ant.tasks;
20  
21  import java.io.File;
22  import java.io.IOException;
23  import java.io.OutputStream;
24  import java.nio.file.Files;
25  import java.util.ArrayList;
26  import java.util.Map;
27  
28  import org.apache.maven.model.RepositoryPolicy;
29  import org.apache.maven.resolver.internal.ant.types.Dependencies;
30  import org.apache.maven.resolver.internal.ant.types.Dependency;
31  import org.apache.maven.resolver.internal.ant.types.DependencyManagement;
32  import org.apache.maven.resolver.internal.ant.types.Pom;
33  import org.apache.maven.resolver.internal.ant.types.model.Developers;
34  import org.apache.maven.resolver.internal.ant.types.model.License;
35  import org.apache.maven.resolver.internal.ant.types.model.Licenses;
36  import org.apache.maven.resolver.internal.ant.types.model.MavenProject;
37  import org.apache.maven.resolver.internal.ant.types.model.Repositories;
38  import org.apache.maven.resolver.internal.ant.types.model.Scm;
39  import org.apache.tools.ant.BuildException;
40  import org.apache.tools.ant.Project;
41  import org.apache.tools.ant.Task;
42  
43  /**
44   * Task to create a Maven POM file.
45   * This task allows you to define dependencies, dependency management, description, licenses, scm, and repositories for the POM.
46   * This is useful if you have defined your dependencies in the ant build script instead of in the POM. This task also
47   * registers the POM. Note: if you want to publish your open source library to maven central using the new
48   * central publisher api, the following 4 additional sections are required in the pom:
49   * description, licenses, developers, and scm.
50   * <h2>Usage Example:</h2>
51   * <pre>{@code
52   * <repo:createPom pomTarget='${pomFile}'
53   *                 dependenciesRef='compile'
54   *                 dependencyManagementRef='dm'
55   *                 name='mylib'
56   *                 description='An useful library'>
57   *   <licenses>
58   *     <license>
59   *       <name>MIT</name>
60   *       <url>https://opensource.org/license/mit</url>
61   *     </license>
62   *   </licenses>
63   *   <developers>
64   *     <developer>
65   *       <id>pnyfelt</id>
66   *       <name>Per Nyfelt</name>
67   *     </developer>
68   *   </developers>
69   *  <scm>
70   *    <url>https://github.com/pnyfelt/example3/tree/master</url>
71   *    <connection>scm:git:https://github.com/pnyfelt/example3.git</connection>
72   *    <developerConnection>scm:git:https://github.com/pnyfelt/example3.git</developerConnection>
73   *  </scm>
74   * </repo:createPom>
75   * }</pre>
76   * <h2>Attributes:</h2>
77   * <ul>
78   *   <li><strong>dependenciesIdRef</strong> - (optional) The reference to the id of the {@code <dependencies>}
79   *   section</li>
80   *   <li><strong>dependencyManagementRef</strong> - (optional) The reference to the id of the
81   *   {@code <dependencyManagement>} section</li>
82   *   <li><strong>pomTarget</strong> - The path to the POM file to create</li>
83   *   <li><strong>groupId</strong> - The group id for the artifact(s), if a <code>groupId</code> property is set this
84   *   can be omitted</li>
85   *   <li><strong>artifactId</strong> - The artifact id for the artifact(s), if an <code>artifactId</code> property is
86   *   set this can be omitted</li>
87   *   <li><strong>version</strong> - The version for the artifact(s), if a <code>version</code> property is set this can
88   *   be omitted</li>
89   *   <li><strong>name</strong> - (optional) The name of the project</li>
90   *   <li><strong>description</strong> - (optional) The short description of the project</li>
91   *   <li><strong>skipPomRegistration</strong> - (optional) If set to true, the pom task will not be called to register
92   *   the pom so must be done explicitly in the build script. Defaults to false.</li>
93   * </ul>
94   * <h2>Nested Elements:</h2>
95   * <ul>
96   *   <li>{@code <licenses>} — (optional) specifies the licenses for the artifact.</li>
97   *   <li>{@code <repositories>} — (optional) defines the additional repositories used for resolving dependencies</li>
98   * </ul>
99   * <h2>Behavior:</h2>
100  * <ul>
101  *  <li>groupId, artifactId and version are required. They could either be defined as properties or passed as
102  * attributes to the createPom task.</li>
103  * </ul>
104  */
105 public class CreatePom extends Task {
106     private String dependenciesIdReference;
107     private String dependencyManagementIdReference;
108     private File pomFile;
109     private String groupId;
110     private String artifactId;
111     private String version;
112     private String name;
113     private String description;
114     private Licenses licenses;
115     private Repositories repositories;
116     private Developers developers;
117     private Scm scm;
118     private boolean skipPomRegistration;
119 
120     /**
121      * Default constructor.
122      */
123     public CreatePom() {
124         // Default constructor
125     }
126 
127     /**
128      * The path to the file that the POM content should be written to.
129      *
130      * @param pomTarget the target file for the POM
131      */
132     public void setPomTarget(String pomTarget) {
133         pomFile = new File(getProject().replaceProperties(pomTarget));
134     }
135 
136     /**
137      * The path to the file that the POM content should be written to.
138      *
139      * @param pomTarget the target file for the POM
140      */
141     public void setPomTarget(File pomTarget) {
142         pomFile = pomTarget;
143     }
144 
145     /**
146      * The reference to the dependency management section.
147      * This should point to the id attribute of a dependencyManagement element
148      * defined elsewhere in the build file.
149      *
150      * @param dependencyManagementRefId the reference to the id of the dependency management section
151      */
152     public void setDependencyManagementRef(String dependencyManagementRefId) {
153         this.dependencyManagementIdReference = getProject().replaceProperties(dependencyManagementRefId);
154     }
155 
156     /**
157      * The reference to the dependencies section.
158      * This should point to the id attribute of a dependencies
159      * element defined elsewhere in the build file.
160      *
161      * @param dependenciesIdReference the reference to the id of the dependencies section
162      */
163     public void setDependenciesRef(String dependenciesIdReference) {
164         this.dependenciesIdReference = getProject().replaceProperties(dependenciesIdReference);
165     }
166 
167     /**
168      * Set the groupId for the POM.
169      * The groupId can include properties that will be replaced at runtime.
170      *
171      * @param groupId the groupId for the POM
172      */
173     public void setGroupId(String groupId) {
174         this.groupId = getProject().replaceProperties(groupId);
175     }
176 
177     /**
178      * Set the artifactId for the POM.
179      * The artifactId can include properties that will be replaced at runtime.
180      *
181      * @param artifactId the artifactId for the POM
182      */
183     public void setArtifactId(String artifactId) {
184         this.artifactId = getProject().replaceProperties(artifactId);
185     }
186 
187     /**
188      * Set the version for the POM.
189      * The version can include properties that will be replaced at runtime.
190      *
191      * @param version the version for the POM
192      */
193     public void setVersion(String version) {
194         this.version = getProject().replaceProperties(version);
195     }
196 
197     /**
198      * Set the name for the POM.
199      * The name can include properties that will be replaced at runtime.
200      *
201      * @param name the name for the POM
202      */
203     public void setName(String name) {
204         this.name = getProject().replaceProperties(name);
205     }
206 
207     /**
208      * Get the description for the POM.
209      *
210      * @return the description for the POM
211      */
212     public String getName() {
213         return name;
214     }
215 
216     /**
217      * Set the description for the POM.
218      * The description can include properties that will be replaced at runtime.
219      *
220      * @param description the description for the POM
221      */
222     public void setDescription(String description) {
223         this.description = getProject().replaceProperties(description);
224     }
225 
226     /**
227      * skipPomRegistration is a flag to indicate whether the POM should be registered in the project after creation.
228      * Setting this to false is equivalent to doing
229      * <pre><code>
230      *   &lt;pom file="${pomFile}"/&gt;
231      * </code></pre>
232      * Default is false, meaning the POM will be registered.
233      *
234      * @param skipPomRegistration true to skip registration, false to register the POM
235      */
236     public void setSkipPomRegistration(boolean skipPomRegistration) {
237         this.skipPomRegistration = skipPomRegistration;
238     }
239 
240     /**
241      * Get the groupId.
242      * If the groupId is not set, it will return a default value based on the project property 'groupId'.
243      *
244      * @return the groupId for the POM
245      */
246     public String getGroupId() {
247         if (groupId == null) {
248             return getProject().getProperty("groupId");
249         }
250         return groupId;
251     }
252 
253     /**
254      * Get the artifactId.
255      * If the artifactId is not set, it will return a default value based on the project property 'artifactId'.
256      *
257      * @return the artifactId for the POM
258      */
259     public String getArtifactId() {
260         if (artifactId == null) {
261             return getProject().getProperty("artifactId");
262         }
263         return artifactId;
264     }
265 
266     /**
267      * Get the version.
268      * If the version is not set, it will return a default value based on the project property 'version'.
269      *
270      * @return the version for the POM
271      */
272     public String getVersion() {
273         if (version == null) {
274             return getProject().getProperty("version");
275         }
276         return version;
277     }
278 
279     /**
280      * Get the description.
281      * If the description is not set, it will return a default value based on the project property 'description'.
282      *
283      * @return the description for the POM
284      */
285     public String getDescription() {
286         return description == null ? super.getDescription() : description;
287     }
288 
289     /**
290      * Add a licenses element to the POM.
291      * This allows you to specify one or more licenses that the project is distributed under.
292      *
293      * @param licenses the licenses to add to the POM
294      */
295     public void addLicenses(Licenses licenses) {
296         this.licenses = licenses;
297     }
298 
299     /**
300      * Add a repositories element to the POM.
301      * This allows you to specify one or more repositories where dependencies can be found. Note that
302      * specifying repositories in the POM is discouraged if you aim to publish to maven central.
303      *
304      * @param repositories the repositories to add to the POM
305      */
306     public void addRepositories(Repositories repositories) {
307         this.repositories = repositories;
308     }
309 
310     /**
311      * Allows ant to add a <code>Developers</code> section.
312      *
313      * @param developers the <code>Developers</code> to add
314      */
315     public void addDevelopers(Developers developers) {
316         this.developers = developers;
317     }
318 
319     /**
320      * Allows ant to add a <code>Scm</code> section.
321      *
322      * @param scm the <code>Scm</code> to add
323      */
324     public void addScm(Scm scm) {
325         this.scm = scm;
326     }
327     /**
328      * Execute the task to create the POM file.
329      * This method will create the POM file with the specified properties and dependencies.
330      */
331     @Override
332     public void execute() {
333         if (!pomFile.getParentFile().exists()) {
334             pomFile.getParentFile().mkdirs();
335         }
336         MavenProject pom = new MavenProject();
337         pom.setGroupId(getGroupId());
338         pom.setArtifactId(getArtifactId());
339         pom.setVersion(getVersion());
340         pom.setName(getName());
341         pom.setDescription(getDescription());
342 
343         if (dependencyManagementIdReference != null) {
344             DependencyManagement dependencyManagement =
345                     DependencyManagement.get(getProject(), dependencyManagementIdReference);
346             appendManagedDependencies(dependencyManagement.getDependencies(), pom);
347         }
348         if (dependenciesIdReference != null) {
349             Dependencies dependencies = new Dependencies();
350             dependencies.setProject(getProject());
351             org.apache.tools.ant.types.Reference ref =
352                     new org.apache.tools.ant.types.Reference(getProject(), dependenciesIdReference);
353             dependencies.setRefid(ref);
354 
355             appendDependencies(dependencies, pom);
356         }
357 
358         if (licenses != null) {
359             for (License l : licenses.getLicenses()) {
360                 pom.addLicense(l);
361             }
362         }
363 
364         if (repositories != null) {
365             repositories.getRepositories().forEach(repository -> {
366                 org.apache.maven.model.Repository repo = new org.apache.maven.model.Repository();
367                 repo.setId(repository.getId());
368                 repo.setName(repository.getName());
369                 repo.setUrl(repository.getUrl());
370                 repo.setLayout(repository.getLayout());
371                 if (repository.getReleases() != null) {
372                     RepositoryPolicy policy = new RepositoryPolicy();
373                     policy.setEnabled(repository.getReleases().isEnabled());
374                     repo.setReleases(policy);
375                 }
376                 if (repository.getSnapshots() != null) {
377                     RepositoryPolicy policy = new RepositoryPolicy();
378                     policy.setEnabled(repository.getSnapshots().isEnabled());
379                     repo.setSnapshots(policy);
380                 }
381                 pom.getModel().getRepositories().add(repo);
382             });
383         }
384 
385         if (developers != null) {
386             developers.getDevelopers().forEach(developer -> {
387                 org.apache.maven.model.Developer dev = new org.apache.maven.model.Developer();
388                 dev.setId(developer.getIdText());
389                 dev.setName(developer.getNameText());
390                 dev.setEmail(developer.getEmailText());
391                 dev.setUrl(developer.getUrlText());
392                 dev.setOrganization(developer.getOrganizationText());
393                 dev.setOrganizationUrl(developer.getOrganizationUrlText());
394                 if (!developer.getRoles().isEmpty()) {
395                     dev.setRoles(new ArrayList<>());
396                     developer.getRoles().forEach(role -> {
397                         dev.getRoles().add(role.getText());
398                     });
399                 }
400                 dev.setTimezone(developer.getTimezoneText());
401                 pom.getModel().getDevelopers().add(dev);
402             });
403         }
404 
405         if (scm != null) {
406             org.apache.maven.model.Scm mavenScm = new org.apache.maven.model.Scm();
407             mavenScm.setConnection(scm.getConnectionText());
408             mavenScm.setDeveloperConnection(scm.getDeveloperConnectionText());
409             mavenScm.setUrl(scm.getUrlText());
410             pom.getModel().setScm(mavenScm);
411         }
412 
413         try (OutputStream pomOutputStream = Files.newOutputStream(pomFile.toPath())) {
414             pom.toPom(pomOutputStream);
415             log("Created the POM file " + pomFile.getAbsolutePath(), Project.MSG_VERBOSE);
416         } catch (IOException e) {
417             throw new BuildException("Failed to create POM file", e);
418         }
419 
420         if (!skipPomRegistration) {
421             registerPom();
422             log("Registered the POM file", Project.MSG_VERBOSE);
423         }
424     }
425 
426     private void registerPom() {
427         Map<String, Class<?>> taskDefs = getProject().getTaskDefinitions();
428         if (!taskDefs.containsKey("pom")) {
429             getProject().addTaskDefinition("pom", Pom.class);
430         }
431         Pom pom = (Pom) getProject().createTask("pom");
432         pom.setProject(getProject());
433         pom.setFile(pomFile);
434         pom.execute();
435     }
436 
437     private static void appendDependencies(Dependencies dependencies, MavenProject pom) {
438 
439         dependencies.getDependencyContainers().forEach(container -> {
440             if (container instanceof Dependency) {
441                 Dependency dep = (Dependency) container;
442                 pom.addDependency(dep);
443             }
444         });
445     }
446 
447     private static void appendManagedDependencies(Dependencies dependencies, MavenProject mavenProject) {
448         dependencies.getDependencyContainers().forEach(it -> {
449             if (it instanceof Dependency) {
450                 Dependency dep = (Dependency) it;
451                 mavenProject.addToDependencyManagement(dep);
452             }
453         });
454     }
455 }