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 * <pom file="${pomFile}"/>
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 }