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                          step.mojos.values().stream()
90                                  .flatMap(m -> m.values().stream())
91                                  .forEach(mojo -> mojo(writer, mojo));
92                      });
93          }
94  
95          writer.accept("=======================================================================");
96      }
97  
98      protected Stream<BuildStep> nonEmptyPredecessors(BuildStep step) {
99          HashSet<BuildStep> preds = new HashSet<>();
100         nonEmptyPredecessors(step, preds, new HashSet<>());
101         return preds.stream();
102     }
103 
104     private void nonEmptyPredecessors(BuildStep step, Set<BuildStep> preds, Set<BuildStep> visited) {
105         if (visited.add(step)) {
106             step.predecessors.forEach(ch -> {
107                 if (ch.executions().findAny().isPresent()) {
108                     preds.add(ch);
109                 } else {
110                     nonEmptyPredecessors(ch, preds, visited);
111                 }
112             });
113         }
114     }
115 
116     protected String phase(MavenProject currentProject, BuildStep step, Set<String> duplicateIds) {
117         if (step.project == currentProject) {
118             return step.name;
119         } else {
120             String artifactId = step.project.getArtifactId();
121             if (duplicateIds.contains(artifactId)) {
122                 return step.name + "(" + step.project.getGroupId() + ":" + artifactId + ")";
123             } else {
124                 return step.name + "(:" + artifactId + ")";
125             }
126         }
127     }
128 
129     protected void mojo(Consumer<String> writer, MojoExecution mojoExecution) {
130         String mojoExecId =
131                 mojoExecution.getGroupId() + ':' + mojoExecution.getArtifactId() + ':' + mojoExecution.getVersion()
132                         + ':' + mojoExecution.getGoal() + " (" + mojoExecution.getExecutionId() + ')';
133 
134         Map<String, List<MojoExecution>> forkedExecutions = mojoExecution.getForkedExecutions();
135         if (!forkedExecutions.isEmpty()) {
136             for (Map.Entry<String, List<MojoExecution>> fork : forkedExecutions.entrySet()) {
137                 writer.accept("\t--- init fork of " + fork.getKey() + " for " + mojoExecId + " ---");
138 
139                 for (MojoExecution forkedExecution : fork.getValue()) {
140                     mojo(writer, forkedExecution);
141                 }
142 
143                 writer.accept("\t--- exit fork of " + fork.getKey() + " for " + mojoExecId + " ---");
144             }
145         }
146 
147         writer.accept("\t\t-----------------------------------------------------------------------");
148         if (mojoExecution.getMojoDescriptor().isAggregator()) {
149             writer.accept("\t\tAggregator goal:        " + mojoExecId);
150         } else {
151             writer.accept("\t\tGoal:                   " + mojoExecId);
152         }
153         if (mojoExecution.getConfiguration() != null) {
154             writer.accept("\t\tConfiguration:          " + mojoExecution.getConfiguration());
155         }
156         if (mojoExecution.getMojoDescriptor().getDependencyCollectionRequired() != null) {
157             writer.accept("\t\tDependencies (collect): "
158                     + mojoExecution.getMojoDescriptor().getDependencyCollectionRequired());
159         }
160         if (mojoExecution.getMojoDescriptor().getDependencyResolutionRequired() != null) {
161             writer.accept("\t\tDependencies (resolve): "
162                     + mojoExecution.getMojoDescriptor().getDependencyResolutionRequired());
163         }
164     }
165 
166     protected String getKey(MavenProject project) {
167         return project.getGroupId() + ':' + project.getArtifactId() + ':' + project.getVersion();
168     }
169 }