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.cling.invoker.mvn;
20  
21  import java.util.Arrays;
22  import java.util.Collection;
23  import java.util.List;
24  import java.util.ListIterator;
25  import java.util.Map;
26  import java.util.Optional;
27  
28  import org.apache.commons.cli.CommandLine;
29  import org.apache.commons.cli.Option;
30  import org.apache.commons.cli.ParseException;
31  import org.apache.maven.api.cli.mvn.MavenOptions;
32  import org.apache.maven.cling.invoker.CommonsCliOptions;
33  import org.codehaus.plexus.interpolation.BasicInterpolator;
34  import org.codehaus.plexus.interpolation.InterpolationException;
35  
36  import static org.apache.maven.cling.invoker.Utils.createInterpolator;
37  
38  public class CommonsCliMavenOptions extends CommonsCliOptions implements MavenOptions {
39      public static CommonsCliMavenOptions parse(String source, String[] args) throws ParseException {
40          CLIManager cliManager = new CLIManager();
41          return new CommonsCliMavenOptions(source, cliManager, cliManager.parse(args));
42      }
43  
44      protected CommonsCliMavenOptions(String source, CLIManager cliManager, CommandLine commandLine) {
45          super(source, cliManager, commandLine);
46      }
47  
48      private static CommonsCliMavenOptions interpolate(
49              CommonsCliMavenOptions options, Collection<Map<String, String>> properties) {
50          try {
51              // now that we have properties, interpolate all arguments
52              BasicInterpolator interpolator = createInterpolator(properties);
53              CommandLine.Builder commandLineBuilder = new CommandLine.Builder();
54              commandLineBuilder.setDeprecatedHandler(o -> {});
55              for (Option option : options.commandLine.getOptions()) {
56                  if (!CLIManager.USER_PROPERTY.equals(option.getOpt())) {
57                      List<String> values = option.getValuesList();
58                      for (ListIterator<String> it = values.listIterator(); it.hasNext(); ) {
59                          it.set(interpolator.interpolate(it.next()));
60                      }
61                  }
62                  commandLineBuilder.addOption(option);
63              }
64              for (String arg : options.commandLine.getArgList()) {
65                  commandLineBuilder.addArg(interpolator.interpolate(arg));
66              }
67              return new CommonsCliMavenOptions(
68                      options.source, (CLIManager) options.cliManager, commandLineBuilder.build());
69          } catch (InterpolationException e) {
70              throw new IllegalArgumentException("Could not interpolate CommonsCliOptions", e);
71          }
72      }
73  
74      @Override
75      public Optional<String> alternatePomFile() {
76          if (commandLine.hasOption(CLIManager.ALTERNATE_POM_FILE)) {
77              return Optional.of(commandLine.getOptionValue(CLIManager.ALTERNATE_POM_FILE));
78          }
79          return Optional.empty();
80      }
81  
82      @Override
83      public Optional<Boolean> offline() {
84          if (commandLine.hasOption(CLIManager.OFFLINE)) {
85              return Optional.of(Boolean.TRUE);
86          }
87          return Optional.empty();
88      }
89  
90      @Override
91      public Optional<Boolean> nonRecursive() {
92          if (commandLine.hasOption(CLIManager.NON_RECURSIVE)) {
93              return Optional.of(Boolean.TRUE);
94          }
95          return Optional.empty();
96      }
97  
98      @Override
99      public Optional<Boolean> updateSnapshots() {
100         if (commandLine.hasOption(CLIManager.UPDATE_SNAPSHOTS)) {
101             return Optional.of(Boolean.TRUE);
102         }
103         return Optional.empty();
104     }
105 
106     @Override
107     public Optional<List<String>> activatedProfiles() {
108         if (commandLine.hasOption(CLIManager.ACTIVATE_PROFILES)) {
109             return Optional.of(Arrays.asList(commandLine.getOptionValues(CLIManager.ACTIVATE_PROFILES)));
110         }
111         return Optional.empty();
112     }
113 
114     @Override
115     public Optional<Boolean> suppressSnapshotUpdates() {
116         if (commandLine.hasOption(CLIManager.SUPPRESS_SNAPSHOT_UPDATES)) {
117             return Optional.of(Boolean.TRUE);
118         }
119         return Optional.empty();
120     }
121 
122     @Override
123     public Optional<Boolean> strictChecksums() {
124         if (commandLine.hasOption(CLIManager.CHECKSUM_FAILURE_POLICY)) {
125             return Optional.of(Boolean.TRUE);
126         }
127         return Optional.empty();
128     }
129 
130     @Override
131     public Optional<Boolean> relaxedChecksums() {
132         if (commandLine.hasOption(CLIManager.CHECKSUM_WARNING_POLICY)) {
133             return Optional.of(Boolean.TRUE);
134         }
135         return Optional.empty();
136     }
137 
138     @Override
139     public Optional<Boolean> failFast() {
140         if (commandLine.hasOption(CLIManager.FAIL_FAST)) {
141             return Optional.of(Boolean.TRUE);
142         }
143         return Optional.empty();
144     }
145 
146     @Override
147     public Optional<Boolean> failAtEnd() {
148         if (commandLine.hasOption(CLIManager.FAIL_AT_END)) {
149             return Optional.of(Boolean.TRUE);
150         }
151         return Optional.empty();
152     }
153 
154     @Override
155     public Optional<Boolean> failNever() {
156         if (commandLine.hasOption(CLIManager.FAIL_NEVER)) {
157             return Optional.of(Boolean.TRUE);
158         }
159         return Optional.empty();
160     }
161 
162     @Override
163     public Optional<Boolean> resume() {
164         if (commandLine.hasOption(CLIManager.RESUME)) {
165             return Optional.of(Boolean.TRUE);
166         }
167         return Optional.empty();
168     }
169 
170     @Override
171     public Optional<String> resumeFrom() {
172         if (commandLine.hasOption(CLIManager.RESUME_FROM)) {
173             return Optional.of(commandLine.getOptionValue(CLIManager.RESUME_FROM));
174         }
175         return Optional.empty();
176     }
177 
178     @Override
179     public Optional<List<String>> projects() {
180         if (commandLine.hasOption(CLIManager.PROJECT_LIST)) {
181             return Optional.of(Arrays.asList(commandLine.getOptionValues(CLIManager.PROJECT_LIST)));
182         }
183         return Optional.empty();
184     }
185 
186     @Override
187     public Optional<Boolean> alsoMake() {
188         if (commandLine.hasOption(CLIManager.ALSO_MAKE)) {
189             return Optional.of(Boolean.TRUE);
190         }
191         return Optional.empty();
192     }
193 
194     @Override
195     public Optional<Boolean> alsoMakeDependents() {
196         if (commandLine.hasOption(CLIManager.ALSO_MAKE_DEPENDENTS)) {
197             return Optional.of(Boolean.TRUE);
198         }
199         return Optional.empty();
200     }
201 
202     @Override
203     public Optional<String> threads() {
204         if (commandLine.hasOption(CLIManager.THREADS)) {
205             return Optional.of(commandLine.getOptionValue(CLIManager.THREADS));
206         }
207         return Optional.empty();
208     }
209 
210     @Override
211     public Optional<String> builder() {
212         if (commandLine.hasOption(CLIManager.BUILDER)) {
213             return Optional.of(commandLine.getOptionValue(CLIManager.BUILDER));
214         }
215         return Optional.empty();
216     }
217 
218     @Override
219     public Optional<Boolean> noTransferProgress() {
220         if (commandLine.hasOption(CLIManager.NO_TRANSFER_PROGRESS)) {
221             return Optional.of(Boolean.TRUE);
222         }
223         return Optional.empty();
224     }
225 
226     @Override
227     public Optional<Boolean> cacheArtifactNotFound() {
228         if (commandLine.hasOption(CLIManager.CACHE_ARTIFACT_NOT_FOUND)) {
229             return Optional.of(Boolean.parseBoolean(commandLine.getOptionValue(CLIManager.CACHE_ARTIFACT_NOT_FOUND)));
230         }
231         return Optional.empty();
232     }
233 
234     @Override
235     public Optional<Boolean> strictArtifactDescriptorPolicy() {
236         if (commandLine.hasOption(CLIManager.STRICT_ARTIFACT_DESCRIPTOR_POLICY)) {
237             return Optional.of(
238                     Boolean.parseBoolean(commandLine.getOptionValue(CLIManager.STRICT_ARTIFACT_DESCRIPTOR_POLICY)));
239         }
240         return Optional.empty();
241     }
242 
243     @Override
244     public Optional<Boolean> ignoreTransitiveRepositories() {
245         if (commandLine.hasOption(CLIManager.IGNORE_TRANSITIVE_REPOSITORIES)) {
246             return Optional.of(Boolean.TRUE);
247         }
248         return Optional.empty();
249     }
250 
251     @Override
252     public Optional<List<String>> goals() {
253         if (!commandLine.getArgList().isEmpty()) {
254             return Optional.of(commandLine.getArgList());
255         }
256         return Optional.empty();
257     }
258 
259     @Override
260     public MavenOptions interpolate(Collection<Map<String, String>> properties) {
261         return interpolate(this, properties);
262     }
263 
264     protected static class CLIManager extends CommonsCliOptions.CLIManager {
265         public static final String ALTERNATE_POM_FILE = "f";
266         public static final String OFFLINE = "o";
267         public static final String NON_RECURSIVE = "N";
268         public static final String UPDATE_SNAPSHOTS = "U";
269         public static final String ACTIVATE_PROFILES = "P";
270         public static final String SUPPRESS_SNAPSHOT_UPDATES = "nsu";
271         public static final String CHECKSUM_FAILURE_POLICY = "C";
272         public static final String CHECKSUM_WARNING_POLICY = "c";
273         public static final String FAIL_FAST = "ff";
274         public static final String FAIL_AT_END = "fae";
275         public static final String FAIL_NEVER = "fn";
276         public static final String RESUME = "r";
277         public static final String RESUME_FROM = "rf";
278         public static final String PROJECT_LIST = "pl";
279         public static final String ALSO_MAKE = "am";
280         public static final String ALSO_MAKE_DEPENDENTS = "amd";
281         public static final String THREADS = "T";
282         public static final String BUILDER = "b";
283         public static final String NO_TRANSFER_PROGRESS = "ntp";
284         public static final String CACHE_ARTIFACT_NOT_FOUND = "canf";
285         public static final String STRICT_ARTIFACT_DESCRIPTOR_POLICY = "sadp";
286         public static final String IGNORE_TRANSITIVE_REPOSITORIES = "itr";
287 
288         @Override
289         protected void prepareOptions(org.apache.commons.cli.Options options) {
290             super.prepareOptions(options);
291             options.addOption(Option.builder(ALTERNATE_POM_FILE)
292                     .longOpt("file")
293                     .hasArg()
294                     .desc("Force the use of an alternate POM file (or directory with pom.xml)")
295                     .build());
296             options.addOption(Option.builder(OFFLINE)
297                     .longOpt("offline")
298                     .desc("Work offline")
299                     .build());
300             options.addOption(Option.builder(NON_RECURSIVE)
301                     .longOpt("non-recursive")
302                     .desc(
303                             "Do not recurse into sub-projects. When used together with -pl, do not recurse into sub-projects of selected aggregators")
304                     .build());
305             options.addOption(Option.builder(UPDATE_SNAPSHOTS)
306                     .longOpt("update-snapshots")
307                     .desc("Forces a check for missing releases and updated snapshots on remote repositories")
308                     .build());
309             options.addOption(Option.builder(ACTIVATE_PROFILES)
310                     .longOpt("activate-profiles")
311                     .desc(
312                             "Comma-delimited list of profiles to activate. Prefixing a profile with ! excludes it, and ? marks it as optional")
313                     .hasArg()
314                     .build());
315             options.addOption(Option.builder(SUPPRESS_SNAPSHOT_UPDATES)
316                     .longOpt("no-snapshot-updates")
317                     .desc("Suppress SNAPSHOT updates")
318                     .build());
319             options.addOption(Option.builder(CHECKSUM_FAILURE_POLICY)
320                     .longOpt("strict-checksums")
321                     .desc("Fail the build if checksums don't match")
322                     .build());
323             options.addOption(Option.builder(CHECKSUM_WARNING_POLICY)
324                     .longOpt("lax-checksums")
325                     .desc("Warn if checksums don't match")
326                     .build());
327             options.addOption(Option.builder(FAIL_FAST)
328                     .longOpt("fail-fast")
329                     .desc("Stop at first failure in reactorized builds")
330                     .build());
331             options.addOption(Option.builder(FAIL_AT_END)
332                     .longOpt("fail-at-end")
333                     .desc("Only fail the build afterwards; allow all non-impacted builds to continue")
334                     .build());
335             options.addOption(Option.builder(FAIL_NEVER)
336                     .longOpt("fail-never")
337                     .desc("NEVER fail the build, regardless of project result")
338                     .build());
339             options.addOption(Option.builder(RESUME)
340                     .longOpt("resume")
341                     .desc(
342                             "Resume reactor from the last failed project, using the resume.properties file in the build directory")
343                     .build());
344             options.addOption(Option.builder(RESUME_FROM)
345                     .longOpt("resume-from")
346                     .hasArg()
347                     .desc("Resume reactor from specified project")
348                     .build());
349             options.addOption(Option.builder(PROJECT_LIST)
350                     .longOpt("projects")
351                     .desc(
352                             "Comma-delimited list of specified reactor projects to build instead of all projects. A project can be specified by [groupId]:artifactId or by its relative path. Prefixing a project with ! excludes it, and ? marks it as optional")
353                     .hasArg()
354                     .build());
355             options.addOption(Option.builder(ALSO_MAKE)
356                     .longOpt("also-make")
357                     .desc("If project list is specified, also build projects required by the list")
358                     .build());
359             options.addOption(Option.builder(ALSO_MAKE_DEPENDENTS)
360                     .longOpt("also-make-dependents")
361                     .desc("If project list is specified, also build projects that depend on projects on the list")
362                     .build());
363             options.addOption(Option.builder(THREADS)
364                     .longOpt("threads")
365                     .hasArg()
366                     .desc("Thread count, for instance 4 (int) or 2C/2.5C (int/float) where C is core multiplied")
367                     .build());
368             options.addOption(Option.builder(BUILDER)
369                     .longOpt("builder")
370                     .hasArg()
371                     .desc("The id of the build strategy to use")
372                     .build());
373             options.addOption(Option.builder(NO_TRANSFER_PROGRESS)
374                     .longOpt("no-transfer-progress")
375                     .desc("Do not display transfer progress when downloading or uploading")
376                     .build());
377             options.addOption(Option.builder(CACHE_ARTIFACT_NOT_FOUND)
378                     .longOpt("cache-artifact-not-found")
379                     .hasArg()
380                     .desc(
381                             "Defines caching behaviour for 'not found' artifacts. Supported values are 'true' (default), 'false'.")
382                     .build());
383             options.addOption(Option.builder(STRICT_ARTIFACT_DESCRIPTOR_POLICY)
384                     .longOpt("strict-artifact-descriptor-policy")
385                     .hasArg()
386                     .desc(
387                             "Defines 'strict' artifact descriptor policy. Supported values are 'true', 'false' (default).")
388                     .build());
389             options.addOption(Option.builder(IGNORE_TRANSITIVE_REPOSITORIES)
390                     .longOpt("ignore-transitive-repositories")
391                     .desc("If set, Maven will ignore remote repositories introduced by transitive dependencies.")
392                     .build());
393         }
394     }
395 }