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.lifecycle.internal.concurrent;
20  
21  import javax.inject.Named;
22  
23  import java.util.Comparator;
24  import java.util.HashSet;
25  import java.util.List;
26  import java.util.Map;
27  import java.util.Optional;
28  import java.util.Set;
29  import java.util.function.Consumer;
30  import java.util.stream.Collectors;
31  import java.util.stream.Stream;
32  
33  import org.apache.maven.plugin.MojoExecution;
34  import org.apache.maven.project.MavenProject;
35  import org.slf4j.Logger;
36  import org.slf4j.LoggerFactory;
37  
38  /**
39   * <p>
40   * Logs debug output from the various lifecycle phases.
41   * </p>
42   * <strong>NOTE:</strong> This class is not part of any public api and can be changed or deleted without prior notice.
43   *
44   * @since 3.0
45   */
46  @Named
47  public class BuildPlanLogger {
48      private final Logger logger = LoggerFactory.getLogger(getClass());
49  
50      public void writePlan(BuildPlan plan) {
51          if (logger.isDebugEnabled()) {
52              writePlan(logger::debug, plan);
53          }
54      }
55  
56      public void writePlan(BuildPlan plan, MavenProject project) {
57          if (logger.isDebugEnabled()) {
58              writePlan(logger::debug, plan, project);
59          }
60      }
61  
62      public void writePlan(Consumer<String> writer, BuildPlan plan) {
63          plan.projects().forEach(project -> writePlan(writer, plan, project));
64      }
65  
66      public void writePlan(Consumer<String> writer, BuildPlan plan, MavenProject project) {
67          writer.accept("=== PROJECT BUILD PLAN ================================================");
68          writer.accept("Project:                     " + getKey(project));
69          writer.accept("Repositories (dependencies): " + project.getRemoteProjectRepositories());
70          writer.accept("Repositories (plugins):      " + project.getRemotePluginRepositories());
71  
72          Optional<BuildStep> planStep = plan.step(project, BuildStep.PLAN);
73          if (planStep.isPresent() && planStep.get().status.get() == BuildStep.PLANNING) {
74              writer.accept("Build plan will be lazily computed");
75          } else {
76              plan.steps(project)
77                      .filter(step ->
78                              step.phase != null && step.executions().findAny().isPresent())
79                      .sorted(Comparator.comparingInt(plan.sortedNodes()::indexOf))
80                      .forEach(step -> {
81                          writer.accept("\t-----------------------------------------------------------------------");
82                          writer.accept("\tPhase:         " + step.name);
83                          if (!step.predecessors.isEmpty()) {
84                              writer.accept("\tPredecessors:  "
85                                      + nonEmptyPredecessors(step)
86                                              .map(n -> phase(project, n, plan.duplicateIds()))
87                                              .collect(Collectors.joining(", ")));
88                          }
89                          /*
90                          if (!node.successors.isEmpty()) {
91                              writer.accept("\tSuccessors:    "
92                                      + node.successors.stream()
93                                              .map(n -> phase(currentProject, n, duplicateIds))
94                                              .collect(Collectors.joining(", ")));
95                          }
96                          */
97                          step.mojos.values().stream()
98                                  .flatMap(m -> m.values().stream())
99                                  .forEach(mojo -> mojo(writer, mojo));
100                     });
101         }
102 
103         writer.accept("=======================================================================");
104     }
105 
106     protected Stream<BuildStep> nonEmptyPredecessors(BuildStep step) {
107         HashSet<BuildStep> preds = new HashSet<>();
108         nonEmptyPredecessors(step, preds, new HashSet<>());
109         return preds.stream();
110     }
111 
112     private void nonEmptyPredecessors(BuildStep step, Set<BuildStep> preds, Set<BuildStep> visited) {
113         if (visited.add(step)) {
114             step.predecessors.forEach(ch -> {
115                 if (ch.executions().findAny().isPresent()) {
116                     preds.add(ch);
117                 } else {
118                     nonEmptyPredecessors(ch, preds, visited);
119                 }
120             });
121         }
122     }
123 
124     protected String phase(MavenProject currentProject, BuildStep step, Set<String> duplicateIds) {
125         if (step.project == currentProject) {
126             return step.name;
127         } else {
128             String artifactId = step.project.getArtifactId();
129             if (duplicateIds.contains(artifactId)) {
130                 return step.name + "(" + step.project.getGroupId() + ":" + artifactId + ")";
131             } else {
132                 return step.name + "(:" + artifactId + ")";
133             }
134         }
135     }
136 
137     protected void mojo(Consumer<String> writer, MojoExecution mojoExecution) {
138         String mojoExecId =
139                 mojoExecution.getGroupId() + ':' + mojoExecution.getArtifactId() + ':' + mojoExecution.getVersion()
140                         + ':' + mojoExecution.getGoal() + " (" + mojoExecution.getExecutionId() + ')';
141 
142         Map<String, List<MojoExecution>> forkedExecutions = mojoExecution.getForkedExecutions();
143         if (!forkedExecutions.isEmpty()) {
144             for (Map.Entry<String, List<MojoExecution>> fork : forkedExecutions.entrySet()) {
145                 writer.accept("\t--- init fork of " + fork.getKey() + " for " + mojoExecId + " ---");
146 
147                 for (MojoExecution forkedExecution : fork.getValue()) {
148                     mojo(writer, forkedExecution);
149                 }
150 
151                 writer.accept("\t--- exit fork of " + fork.getKey() + " for " + mojoExecId + " ---");
152             }
153         }
154 
155         writer.accept("\t\t-----------------------------------------------------------------------");
156         if (mojoExecution.getMojoDescriptor().isAggregator()) {
157             writer.accept("\t\tAggregator goal:        " + mojoExecId);
158         } else {
159             writer.accept("\t\tGoal:                   " + mojoExecId);
160         }
161         if (mojoExecution.getConfiguration() != null) {
162             writer.accept("\t\tConfiguration:          " + mojoExecution.getConfiguration());
163         }
164         if (mojoExecution.getMojoDescriptor().getDependencyCollectionRequired() != null) {
165             writer.accept("\t\tDependencies (collect): "
166                     + mojoExecution.getMojoDescriptor().getDependencyCollectionRequired());
167         }
168         if (mojoExecution.getMojoDescriptor().getDependencyResolutionRequired() != null) {
169             writer.accept("\t\tDependencies (resolve): "
170                     + mojoExecution.getMojoDescriptor().getDependencyResolutionRequired());
171         }
172     }
173 
174     protected String getKey(MavenProject project) {
175         return project.getGroupId() + ':' + project.getArtifactId() + ':' + project.getVersion();
176     }
177 }