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.api;
20
21 import java.nio.file.Path;
22 import java.util.List;
23 import java.util.Optional;
24
25 import org.apache.maven.api.annotations.Experimental;
26 import org.apache.maven.api.annotations.Nonnull;
27 import org.apache.maven.api.annotations.Nullable;
28 import org.apache.maven.api.model.Build;
29 import org.apache.maven.api.model.Model;
30 import org.apache.maven.api.model.Profile;
31
32 /**
33 * Interface representing a Maven project which can be created using the
34 * {@link org.apache.maven.api.services.ProjectBuilder ProjectBuilder} service.
35 * Such objects are immutable and plugin that wish to modify such objects
36 * need to do so using the {@link org.apache.maven.api.services.ProjectManager
37 * ProjectManager} service.
38 * <p>
39 * Projects are created using the {@code ProjectBuilder} from a POM file
40 * (usually named {@code pom.xml}) on the file system.
41 * The {@link #getPomPath()} will point to the POM file and the
42 * {@link #getBasedir()} to the directory parent containing the
43 * POM file.
44 * </p>
45 *
46 * @since 4.0.0
47 * @see org.apache.maven.api.services.ProjectManager
48 * @see org.apache.maven.api.services.ProjectBuilder
49 */
50 @Experimental
51 public interface Project {
52
53 /**
54 * {@return the project groupId}.
55 */
56 @Nonnull
57 String getGroupId();
58
59 /**
60 * {@return the project artifactId}.
61 */
62 @Nonnull
63 String getArtifactId();
64
65 /**
66 * {@return the project version}.
67 */
68 @Nonnull
69 String getVersion();
70
71 /**
72 * {@return the project packaging}.
73 * <p>
74 * Note: unlike in legacy code, logical checks against string representing packaging (returned by this method)
75 * are NOT recommended (code like {@code "pom".equals(project.getPackaging)} must be avoided). Use method
76 * {@link #getArtifacts()} to gain access to POM or build artifact.
77 *
78 * @see #getArtifacts()
79 */
80 @Nonnull
81 Packaging getPackaging();
82
83 /**
84 * {@return the project language}. It is by default determined by {@link #getPackaging()}.
85 *
86 * @see #getPackaging()
87 */
88 @Nonnull
89 default Language getLanguage() {
90 return getPackaging().language();
91 }
92
93 /**
94 * {@return the project POM artifact}, which is the artifact of the POM of this project. Every project have a POM
95 * artifact, even if the existence of backing POM file is NOT a requirement (i.e. for some transient projects).
96 *
97 * @see org.apache.maven.api.services.ArtifactManager#getPath(Artifact)
98 */
99 @Nonnull
100 default ProducedArtifact getPomArtifact() {
101 return getArtifacts().get(0);
102 }
103
104 /**
105 * {@return the project main artifact}, which is the artifact produced by this project build, if applicable.
106 * This artifact MAY be absent if the project is actually not producing any main artifact (i.e. "pom" packaging).
107 *
108 * @see #getPackaging()
109 * @see org.apache.maven.api.services.ArtifactManager#getPath(Artifact)
110 */
111 @Nonnull
112 default Optional<ProducedArtifact> getMainArtifact() {
113 List<ProducedArtifact> artifacts = getArtifacts();
114 return artifacts.size() == 2 ? Optional.of(artifacts.get(1)) : Optional.empty();
115 }
116
117 /**
118 * {@return the project artifacts as immutable list}. Elements are the project POM artifact and the artifact
119 * produced by this project build, if applicable. Hence, the returned list may have one or two elements
120 * (never less than 1, never more than 2), depending on project packaging.
121 * <p>
122 * The list's first element is ALWAYS the project POM artifact. Presence of second element in the list depends
123 * solely on the project packaging.
124 *
125 * @see #getPackaging()
126 * @see #getPomArtifact()
127 * @see #getMainArtifact()
128 * @see org.apache.maven.api.services.ArtifactManager#getPath(Artifact)
129 */
130 @Nonnull
131 List<ProducedArtifact> getArtifacts();
132
133 /**
134 * {@return the project model}.
135 */
136 @Nonnull
137 Model getModel();
138
139 /**
140 * Shorthand method.
141 *
142 * @return the build element of the project model
143 */
144 @Nonnull
145 default Build getBuild() {
146 Build build = getModel().getBuild();
147 return build != null ? build : Build.newInstance();
148 }
149
150 /**
151 * Returns the path to the pom file for this project.
152 * A project is usually read from a file named {@code pom.xml},
153 * which contains the {@linkplain #getModel() model} in an XML form.
154 * When a custom {@code org.apache.maven.api.spi.ModelParser} is used,
155 * the path may point to a non XML file.
156 * <p>
157 * The POM path is also used to define the {@linkplain #getBasedir() base directory}
158 * of the project.
159 *
160 * @return the path of the pom
161 * @see #getBasedir()
162 */
163 @Nonnull
164 Path getPomPath();
165
166 /**
167 * Returns the project base directory, i.e. the directory containing the project.
168 * A project is usually read from the file system and this will point to
169 * the directory containing the POM file.
170 *
171 * @return the path of the directory containing the project
172 */
173 @Nonnull
174 Path getBasedir();
175
176 /**
177 * {@return the absolute path to the directory where files generated by the build are placed}
178 * <p>
179 * <strong>Purpose:</strong> This method provides the base output directory for a given scope,
180 * which serves as the destination for compiled classes, processed resources, and other generated files.
181 * The returned path is always absolute.
182 * </p>
183 * <p>
184 * <strong>Scope-based Directory Resolution:</strong>
185 * </p>
186 * <table class="striped">
187 * <caption>Output Directory by Scope</caption>
188 * <thead>
189 * <tr>
190 * <th>Scope Parameter</th>
191 * <th>Build Configuration</th>
192 * <th>Typical Path</th>
193 * <th>Contents</th>
194 * </tr>
195 * </thead>
196 * <tbody>
197 * <tr>
198 * <td>{@link ProjectScope#MAIN}</td>
199 * <td>{@code build.getOutputDirectory()}</td>
200 * <td>{@code target/classes}</td>
201 * <td>Compiled application classes and processed main resources</td>
202 * </tr>
203 * <tr>
204 * <td>{@link ProjectScope#TEST}</td>
205 * <td>{@code build.getTestOutputDirectory()}</td>
206 * <td>{@code target/test-classes}</td>
207 * <td>Compiled test classes and processed test resources</td>
208 * </tr>
209 * <tr>
210 * <td>{@code null} or other</td>
211 * <td>{@code build.getDirectory()}</td>
212 * <td>{@code target}</td>
213 * <td>Parent directory for all build outputs</td>
214 * </tr>
215 * </tbody>
216 * </table>
217 * <p>
218 * <strong>Role in {@link SourceRoot} Path Resolution:</strong>
219 * </p>
220 * <p>
221 * This method is the foundation for {@link SourceRoot#targetPath(Project)} path resolution.
222 * When a {@link SourceRoot} has a relative {@code targetPath}, it is resolved against the
223 * output directory returned by this method for the source root's scope. This ensures that:
224 * </p>
225 * <ul>
226 * <li>Main resources with {@code targetPath="META-INF"} are copied to {@code target/classes/META-INF}</li>
227 * <li>Test resources with {@code targetPath="test-data"} are copied to {@code target/test-classes/test-data}</li>
228 * <li>Resources without an explicit {@code targetPath} are copied to the root of the output directory</li>
229 * </ul>
230 * <p>
231 * <strong>Maven 3 Compatibility:</strong>
232 * </p>
233 * <p>
234 * This behavior maintains the Maven 3.x semantic where resource {@code targetPath} elements
235 * are resolved relative to the appropriate output directory ({@code project.build.outputDirectory}
236 * or {@code project.build.testOutputDirectory}), <strong>not</strong> the project base directory.
237 * </p>
238 * <p>
239 * In Maven 3, when a resource configuration specifies:
240 * </p>
241 * <pre>{@code
242 * <resource>
243 * <directory>src/main/resources</directory>
244 * <targetPath>META-INF/resources</targetPath>
245 * </resource>
246 * }</pre>
247 * <p>
248 * The maven-resources-plugin resolves {@code targetPath} as:
249 * {@code project.build.outputDirectory + "/" + targetPath}, which results in
250 * {@code target/classes/META-INF/resources}. This method provides the same base directory
251 * ({@code target/classes}) for Maven 4 API consumers.
252 * </p>
253 * <p>
254 * <strong>Example:</strong>
255 * </p>
256 * <pre>{@code
257 * Project project = ...; // project at /home/user/myproject
258 *
259 * // Get main output directory
260 * Path mainOutput = project.getOutputDirectory(ProjectScope.MAIN);
261 * // Result: /home/user/myproject/target/classes
262 *
263 * // Get test output directory
264 * Path testOutput = project.getOutputDirectory(ProjectScope.TEST);
265 * // Result: /home/user/myproject/target/test-classes
266 *
267 * // Get build directory
268 * Path buildDir = project.getOutputDirectory(null);
269 * // Result: /home/user/myproject/target
270 * }</pre>
271 *
272 * @param scope the scope of the generated files for which to get the directory, or {@code null} for the build directory
273 * @return the absolute path to the output directory for the given scope
274 *
275 * @see SourceRoot#targetPath(Project)
276 * @see SourceRoot#targetPath()
277 * @see Build#getOutputDirectory()
278 * @see Build#getTestOutputDirectory()
279 * @see Build#getDirectory()
280 */
281 @Nonnull
282 default Path getOutputDirectory(@Nullable ProjectScope scope) {
283 String dir;
284 Build build = getBuild();
285 if (scope == ProjectScope.MAIN) {
286 dir = build.getOutputDirectory();
287 } else if (scope == ProjectScope.TEST) {
288 dir = build.getTestOutputDirectory();
289 } else {
290 dir = build.getDirectory();
291 }
292 return getBasedir().resolve(dir);
293 }
294
295 /**
296 * {@return the project direct dependencies (directly specified or inherited)}.
297 */
298 @Nonnull
299 List<DependencyCoordinates> getDependencies();
300
301 /**
302 * {@return the project managed dependencies (directly specified or inherited)}.
303 */
304 @Nonnull
305 List<DependencyCoordinates> getManagedDependencies();
306
307 /**
308 * {@return the project ID, usable as key}.
309 */
310 @Nonnull
311 default String getId() {
312 return getModel().getId();
313 }
314
315 /**
316 * Returns a boolean indicating if the project is the top level project for
317 * this reactor build. The top level project may be different from the
318 * {@code rootDirectory}, especially if a subtree of the project is being
319 * built, either because Maven has been launched in a subdirectory or using
320 * a {@code -f} option.
321 *
322 * @return {@code true} if the project is the top level project for this build
323 */
324 boolean isTopProject();
325
326 /**
327 * Returns a boolean indicating if the project is a root project,
328 * meaning that the {@link #getRootDirectory()} and {@link #getBasedir()}
329 * points to the same directory, and that either {@link Model#isRoot()}
330 * is {@code true} or that {@code basedir} contains a {@code .mvn} child
331 * directory.
332 *
333 * @return {@code true} if the project is the root project
334 * @see Model#isRoot()
335 */
336 boolean isRootProject();
337
338 /**
339 * Gets the root directory of the project, which is the parent directory
340 * containing the {@code .mvn} directory or flagged with {@code root="true"}.
341 *
342 * @return the root directory of the project
343 * @throws IllegalStateException if the root directory could not be found
344 * @see Session#getRootDirectory()
345 */
346 @Nonnull
347 Path getRootDirectory();
348
349 /**
350 * Returns project parent project, if any.
351 * <p>
352 * Note that the model may have a parent defined, but an empty parent
353 * project may be returned if the parent comes from a remote repository,
354 * as a {@code Project} must refer to a buildable project.
355 *
356 * @return an optional containing the parent project
357 * @see Model#getParent()
358 */
359 @Nonnull
360 Optional<Project> getParent();
361
362 /**
363 * Returns all profiles defined in this project.
364 * <p>
365 * This method returns only the profiles defined directly in the current project's POM
366 * and does not include profiles from parent projects.
367 *
368 * @return a non-null, possibly empty list of profiles defined in this project
369 * @see Profile
370 * @see #getEffectiveProfiles()
371 */
372 @Nonnull
373 List<Profile> getDeclaredProfiles();
374
375 /**
376 * Returns all profiles defined in this project and all of its parent projects.
377 * <p>
378 * This method traverses the parent hierarchy and includes profiles defined in parent POMs.
379 * The returned list contains profiles from the current project and all of its ancestors in
380 * the project inheritance chain.
381 *
382 * @return a non-null, possibly empty list of all profiles from this project and its parents
383 * @see Profile
384 * @see #getDeclaredProfiles()
385 */
386 @Nonnull
387 List<Profile> getEffectiveProfiles();
388
389 /**
390 * Returns all active profiles for the current project build.
391 * <p>
392 * Active profiles are those that have been explicitly activated through one of the following means:
393 * <ul>
394 * <li>Command line activation using the -P flag</li>
395 * <li>Maven settings activation in settings.xml via <activeProfiles></li>
396 * <li>Automatic activation via <activation> conditions</li>
397 * <li>The default active profile (marked with <activeByDefault>true</activeByDefault>)</li>
398 * </ul>
399 * <p>
400 * The active profiles control various aspects of the build configuration including but not
401 * limited to dependencies, plugins, properties, and build resources.
402 *
403 * @return a non-null, possibly empty list of active profiles for this project
404 * @see Profile
405 * @see #getEffectiveActiveProfiles()
406 */
407 @Nonnull
408 List<Profile> getDeclaredActiveProfiles();
409
410 /**
411 * Returns all active profiles for this project and all of its parent projects.
412 * <p>
413 * This method traverses the parent hierarchy and collects all active profiles from
414 * the current project and its ancestors. Active profiles are those that meet the
415 * activation criteria through explicit activation or automatic conditions.
416 * <p>
417 * The combined set of active profiles from the entire project hierarchy affects
418 * the effective build configuration.
419 *
420 * @return a non-null, possibly empty list of all active profiles from this project and its parents
421 * @see Profile
422 * @see #getDeclaredActiveProfiles()
423 */
424 @Nonnull
425 List<Profile> getEffectiveActiveProfiles();
426 }