1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.cling.invoker;
20
21 import java.io.PrintWriter;
22 import java.io.StringWriter;
23 import java.util.LinkedHashSet;
24 import java.util.List;
25 import java.util.ListIterator;
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.function.UnaryOperator;
31
32 import org.apache.commons.cli.CommandLine;
33 import org.apache.commons.cli.DefaultParser;
34 import org.apache.commons.cli.DeprecatedAttributes;
35 import org.apache.commons.cli.HelpFormatter;
36 import org.apache.commons.cli.Option;
37 import org.apache.commons.cli.ParseException;
38 import org.apache.maven.api.cli.Options;
39 import org.apache.maven.api.cli.ParserRequest;
40 import org.apache.maven.api.services.Interpolator;
41 import org.apache.maven.api.services.InterpolatorException;
42 import org.apache.maven.jline.MessageUtils;
43
44 import static java.util.Objects.requireNonNull;
45 import static org.apache.maven.cling.invoker.CliUtils.createInterpolator;
46 import static org.apache.maven.cling.invoker.CliUtils.toMap;
47
48 public class CommonsCliOptions implements Options {
49 public static CommonsCliOptions parse(String source, String[] args) throws ParseException {
50 CLIManager cliManager = new CLIManager();
51 return new CommonsCliOptions(source, cliManager, cliManager.parse(args));
52 }
53
54 protected final String source;
55 protected final CLIManager cliManager;
56 protected final CommandLine commandLine;
57
58 protected CommonsCliOptions(String source, CLIManager cliManager, CommandLine commandLine) {
59 this.source = requireNonNull(source);
60 this.cliManager = requireNonNull(cliManager);
61 this.commandLine = requireNonNull(commandLine);
62 }
63
64 @Override
65 public String source() {
66 return source;
67 }
68
69 @Override
70 public Optional<Map<String, String>> userProperties() {
71 if (commandLine.hasOption(CLIManager.USER_PROPERTY)) {
72 return Optional.of(toMap(commandLine.getOptionProperties(CLIManager.USER_PROPERTY)));
73 }
74 return Optional.empty();
75 }
76
77 @Override
78 public Optional<Boolean> showVersionAndExit() {
79 if (commandLine.hasOption(CLIManager.SHOW_VERSION_AND_EXIT)) {
80 return Optional.of(Boolean.TRUE);
81 }
82 return Optional.empty();
83 }
84
85 @Override
86 public Optional<Boolean> showVersion() {
87 if (commandLine.hasOption(CLIManager.SHOW_VERSION)) {
88 return Optional.of(Boolean.TRUE);
89 }
90 return Optional.empty();
91 }
92
93 @Override
94 public Optional<Boolean> quiet() {
95 if (commandLine.hasOption(CLIManager.QUIET)) {
96 return Optional.of(Boolean.TRUE);
97 }
98 return Optional.empty();
99 }
100
101 @Override
102 public Optional<Boolean> verbose() {
103 if (commandLine.hasOption(CLIManager.VERBOSE)) {
104 return Optional.of(Boolean.TRUE);
105 }
106 return Optional.empty();
107 }
108
109 @Override
110 public Optional<Boolean> showErrors() {
111 if (commandLine.hasOption(CLIManager.SHOW_ERRORS) || verbose().orElse(false)) {
112 return Optional.of(Boolean.TRUE);
113 }
114 return Optional.empty();
115 }
116
117 @Override
118 public Optional<String> failOnSeverity() {
119 if (commandLine.hasOption(CLIManager.FAIL_ON_SEVERITY)) {
120 return Optional.of(commandLine.getOptionValue(CLIManager.FAIL_ON_SEVERITY));
121 }
122 return Optional.empty();
123 }
124
125 @Override
126 public Optional<Boolean> nonInteractive() {
127 if (commandLine.hasOption(CLIManager.NON_INTERACTIVE) || commandLine.hasOption(CLIManager.BATCH_MODE)) {
128 return Optional.of(Boolean.TRUE);
129 }
130 return Optional.empty();
131 }
132
133 @Override
134 public Optional<Boolean> forceInteractive() {
135 if (commandLine.hasOption(CLIManager.FORCE_INTERACTIVE)) {
136 return Optional.of(Boolean.TRUE);
137 }
138 return Optional.empty();
139 }
140
141 @Override
142 public Optional<String> altUserSettings() {
143 if (commandLine.hasOption(CLIManager.ALTERNATE_USER_SETTINGS)) {
144 return Optional.of(commandLine.getOptionValue(CLIManager.ALTERNATE_USER_SETTINGS));
145 }
146 return Optional.empty();
147 }
148
149 @Override
150 public Optional<String> altProjectSettings() {
151 if (commandLine.hasOption(CLIManager.ALTERNATE_PROJECT_SETTINGS)) {
152 return Optional.of(commandLine.getOptionValue(CLIManager.ALTERNATE_PROJECT_SETTINGS));
153 }
154 return Optional.empty();
155 }
156
157 @Override
158 public Optional<String> altInstallationSettings() {
159 if (commandLine.hasOption(CLIManager.ALTERNATE_INSTALLATION_SETTINGS)) {
160 return Optional.of(commandLine.getOptionValue(CLIManager.ALTERNATE_INSTALLATION_SETTINGS));
161 }
162 if (commandLine.hasOption(CLIManager.ALTERNATE_GLOBAL_SETTINGS)) {
163 return Optional.of(commandLine.getOptionValue(CLIManager.ALTERNATE_GLOBAL_SETTINGS));
164 }
165 return Optional.empty();
166 }
167
168 @Override
169 public Optional<String> altUserToolchains() {
170 if (commandLine.hasOption(CLIManager.ALTERNATE_USER_TOOLCHAINS)) {
171 return Optional.of(commandLine.getOptionValue(CLIManager.ALTERNATE_USER_TOOLCHAINS));
172 }
173 return Optional.empty();
174 }
175
176 @Override
177 public Optional<String> altInstallationToolchains() {
178 if (commandLine.hasOption(CLIManager.ALTERNATE_INSTALLATION_TOOLCHAINS)) {
179 return Optional.of(commandLine.getOptionValue(CLIManager.ALTERNATE_INSTALLATION_TOOLCHAINS));
180 }
181 if (commandLine.hasOption(CLIManager.ALTERNATE_GLOBAL_TOOLCHAINS)) {
182 return Optional.of(commandLine.getOptionValue(CLIManager.ALTERNATE_GLOBAL_TOOLCHAINS));
183 }
184 return Optional.empty();
185 }
186
187 @Override
188 public Optional<String> logFile() {
189 if (commandLine.hasOption(CLIManager.LOG_FILE)) {
190 return Optional.of(commandLine.getOptionValue(CLIManager.LOG_FILE));
191 }
192 return Optional.empty();
193 }
194
195 @Override
196 public Optional<Boolean> rawStreams() {
197 if (commandLine.hasOption(CLIManager.RAW_STREAMS)) {
198 return Optional.of(Boolean.TRUE);
199 }
200 return Optional.empty();
201 }
202
203 @Override
204 public Optional<String> color() {
205 if (commandLine.hasOption(CLIManager.COLOR)) {
206 if (commandLine.getOptionValue(CLIManager.COLOR) != null) {
207 return Optional.of(commandLine.getOptionValue(CLIManager.COLOR));
208 } else {
209 return Optional.of("auto");
210 }
211 }
212 return Optional.empty();
213 }
214
215 @Override
216 public Optional<Boolean> offline() {
217 if (commandLine.hasOption(CLIManager.OFFLINE)) {
218 return Optional.of(Boolean.TRUE);
219 }
220 return Optional.empty();
221 }
222
223 @Override
224 public Optional<Boolean> help() {
225 if (commandLine.hasOption(CLIManager.HELP)) {
226 return Optional.of(Boolean.TRUE);
227 }
228 return Optional.empty();
229 }
230
231 @Override
232 public void warnAboutDeprecatedOptions(ParserRequest request, Consumer<String> printWriter) {
233 if (cliManager.getUsedDeprecatedOptions().isEmpty()) {
234 return;
235 }
236 printWriter.accept("Detected deprecated option use in " + source);
237 for (Option option : cliManager.getUsedDeprecatedOptions()) {
238 StringBuilder sb = new StringBuilder();
239 sb.append("The option ");
240 if (option.getOpt() != null) {
241 sb.append("-").append(option.getOpt());
242 }
243 if (option.getLongOpt() != null) {
244 if (option.getOpt() != null) {
245 sb.append(",");
246 }
247 sb.append("--").append(option.getLongOpt());
248 }
249 sb.append(" is deprecated ");
250 if (option.getDeprecated().isForRemoval()) {
251 sb.append("and will be removed in a future version");
252 }
253 if (option.getDeprecated().getSince() != null) {
254 sb.append(" since ")
255 .append(request.commandName())
256 .append(" ")
257 .append(option.getDeprecated().getSince());
258 }
259 printWriter.accept(sb.toString());
260 }
261 }
262
263 @Override
264 public final Options interpolate(UnaryOperator<String> callback) {
265 try {
266
267 Interpolator interpolator = createInterpolator();
268 CommandLine.Builder commandLineBuilder = CommandLine.builder();
269 commandLineBuilder.setDeprecatedHandler(o -> {});
270 for (Option option : commandLine.getOptions()) {
271 if (!CommonsCliOptions.CLIManager.USER_PROPERTY.equals(option.getOpt())) {
272 List<String> values = option.getValuesList();
273 for (ListIterator<String> it = values.listIterator(); it.hasNext(); ) {
274 it.set(interpolator.interpolate(it.next(), callback));
275 }
276 }
277 commandLineBuilder.addOption(option);
278 }
279 for (String arg : commandLine.getArgList()) {
280 commandLineBuilder.addArg(interpolator.interpolate(arg, callback));
281 }
282 return copy(source, cliManager, commandLineBuilder.build());
283 } catch (InterpolatorException e) {
284 throw new IllegalArgumentException("Could not interpolate CommonsCliOptions", e);
285 }
286 }
287
288 protected CommonsCliOptions copy(String source, CLIManager cliManager, CommandLine commandLine) {
289 return new CommonsCliOptions(source, cliManager, commandLine);
290 }
291
292 @Override
293 public void displayHelp(ParserRequest request, Consumer<String> printStream) {
294 cliManager.displayHelp(request.command(), printStream);
295 }
296
297 protected static class CLIManager {
298 public static final String USER_PROPERTY = "D";
299 public static final String SHOW_VERSION_AND_EXIT = "v";
300 public static final String SHOW_VERSION = "V";
301 public static final String QUIET = "q";
302 public static final String VERBOSE = "X";
303
304 public static final String SHOW_ERRORS = "e";
305
306 public static final String FAIL_ON_SEVERITY = "fos";
307 public static final String NON_INTERACTIVE = "non-interactive";
308 public static final String BATCH_MODE = "B";
309 public static final String FORCE_INTERACTIVE = "force-interactive";
310 public static final String ALTERNATE_USER_SETTINGS = "s";
311 public static final String ALTERNATE_PROJECT_SETTINGS = "ps";
312 public static final String ALTERNATE_INSTALLATION_SETTINGS = "is";
313 public static final String ALTERNATE_USER_TOOLCHAINS = "t";
314 public static final String ALTERNATE_INSTALLATION_TOOLCHAINS = "it";
315 public static final String LOG_FILE = "l";
316 public static final String RAW_STREAMS = "raw-streams";
317 public static final String COLOR = "color";
318 public static final String OFFLINE = "o";
319 public static final String HELP = "h";
320
321
322 public static final String SHOW_ERRORS_CLI_ARG = "-" + SHOW_ERRORS;
323
324
325 public static final String DEBUG = "debug";
326 public static final String ENC = "enc";
327 public static final String UPGRADE = "up";
328 public static final String SHELL = "shell";
329 public static final String YJP = "yjp";
330
331
332 @Deprecated
333 public static final String ALTERNATE_GLOBAL_SETTINGS = "gs";
334
335 @Deprecated
336 public static final String ALTERNATE_GLOBAL_TOOLCHAINS = "gt";
337
338 protected org.apache.commons.cli.Options options;
339 protected final LinkedHashSet<Option> usedDeprecatedOptions = new LinkedHashSet<>();
340
341 @SuppressWarnings("checkstyle:MethodLength")
342 protected CLIManager() {
343 options = new org.apache.commons.cli.Options();
344 prepareOptions(options);
345 }
346
347 protected void prepareOptions(org.apache.commons.cli.Options options) {
348 options.addOption(Option.builder(HELP)
349 .longOpt("help")
350 .desc("Display help information")
351 .build());
352 options.addOption(Option.builder(USER_PROPERTY)
353 .numberOfArgs(2)
354 .valueSeparator('=')
355 .desc("Define a user property")
356 .build());
357 options.addOption(Option.builder(SHOW_VERSION_AND_EXIT)
358 .longOpt("version")
359 .desc("Display version information")
360 .build());
361 options.addOption(Option.builder(QUIET)
362 .longOpt("quiet")
363 .desc("Quiet output - only show errors")
364 .build());
365 options.addOption(Option.builder(VERBOSE)
366 .longOpt("verbose")
367 .desc("Produce execution verbose output")
368 .build());
369 options.addOption(Option.builder(SHOW_ERRORS)
370 .longOpt("errors")
371 .desc("Produce execution error messages")
372 .build());
373 options.addOption(Option.builder(BATCH_MODE)
374 .longOpt("batch-mode")
375 .desc("Run in non-interactive mode. Alias for --non-interactive (kept for backwards compatability)")
376 .build());
377 options.addOption(Option.builder()
378 .longOpt(NON_INTERACTIVE)
379 .desc("Run in non-interactive mode. Alias for --batch-mode")
380 .build());
381 options.addOption(Option.builder()
382 .longOpt(FORCE_INTERACTIVE)
383 .desc(
384 "Run in interactive mode. Overrides, if applicable, the CI environment variable and --non-interactive/--batch-mode options")
385 .build());
386 options.addOption(Option.builder(ALTERNATE_USER_SETTINGS)
387 .longOpt("settings")
388 .desc("Alternate path for the user settings file")
389 .hasArg()
390 .build());
391 options.addOption(Option.builder(ALTERNATE_PROJECT_SETTINGS)
392 .longOpt("project-settings")
393 .desc("Alternate path for the project settings file")
394 .hasArg()
395 .build());
396 options.addOption(Option.builder(ALTERNATE_INSTALLATION_SETTINGS)
397 .longOpt("install-settings")
398 .desc("Alternate path for the installation settings file")
399 .hasArg()
400 .build());
401 options.addOption(Option.builder(ALTERNATE_USER_TOOLCHAINS)
402 .longOpt("toolchains")
403 .desc("Alternate path for the user toolchains file")
404 .hasArg()
405 .build());
406 options.addOption(Option.builder(ALTERNATE_INSTALLATION_TOOLCHAINS)
407 .longOpt("install-toolchains")
408 .desc("Alternate path for the installation toolchains file")
409 .hasArg()
410 .build());
411 options.addOption(Option.builder(FAIL_ON_SEVERITY)
412 .longOpt("fail-on-severity")
413 .desc("Configure which severity of logging should cause the build to fail")
414 .hasArg()
415 .build());
416 options.addOption(Option.builder(LOG_FILE)
417 .longOpt("log-file")
418 .hasArg()
419 .desc("Log file where all build output will go (disables output color)")
420 .build());
421 options.addOption(Option.builder()
422 .longOpt(RAW_STREAMS)
423 .desc("Do not decorate standard output and error streams")
424 .build());
425 options.addOption(Option.builder(SHOW_VERSION)
426 .longOpt("show-version")
427 .desc("Display version information WITHOUT stopping build")
428 .build());
429 options.addOption(Option.builder()
430 .longOpt(COLOR)
431 .hasArg()
432 .optionalArg(true)
433 .desc("Defines the color mode of the output. Supported are 'auto', 'always', 'never'.")
434 .build());
435 options.addOption(Option.builder(OFFLINE)
436 .longOpt("offline")
437 .desc("Work offline")
438 .build());
439
440
441 options.addOption(Option.builder()
442 .longOpt(DEBUG)
443 .desc("Launch the JVM in debug mode (script option).")
444 .build());
445 options.addOption(Option.builder()
446 .longOpt(ENC)
447 .desc("Launch the Maven Encryption tool (script option).")
448 .build());
449 options.addOption(Option.builder()
450 .longOpt(UPGRADE)
451 .desc("Launch the Maven Upgrade tool (script option).")
452 .build());
453 options.addOption(Option.builder()
454 .longOpt(SHELL)
455 .desc("Launch the Maven Shell tool (script option).")
456 .build());
457 options.addOption(Option.builder()
458 .longOpt(YJP)
459 .desc("Launch the JVM with Yourkit profiler (script option).")
460 .build());
461
462
463 options.addOption(Option.builder(ALTERNATE_GLOBAL_SETTINGS)
464 .longOpt("global-settings")
465 .desc("<deprecated> Alternate path for the global settings file.")
466 .hasArg()
467 .deprecated(DeprecatedAttributes.builder()
468 .setForRemoval(true)
469 .setSince("4.0.0")
470 .setDescription("Use -is,--install-settings instead.")
471 .get())
472 .build());
473 options.addOption(Option.builder(ALTERNATE_GLOBAL_TOOLCHAINS)
474 .longOpt("global-toolchains")
475 .desc("<deprecated> Alternate path for the global toolchains file.")
476 .hasArg()
477 .deprecated(DeprecatedAttributes.builder()
478 .setForRemoval(true)
479 .setSince("4.0.0")
480 .setDescription("Use -it,--install-toolchains instead.")
481 .get())
482 .build());
483 }
484
485 public CommandLine parse(String[] args) throws ParseException {
486
487 String[] cleanArgs = CleanArgument.cleanArgs(args);
488 DefaultParser parser = DefaultParser.builder()
489 .setDeprecatedHandler(this::addDeprecatedOption)
490 .build();
491 CommandLine commandLine = parser.parse(options, cleanArgs);
492
493 options.getOptions().forEach(commandLine::hasOption);
494 return commandLine;
495 }
496
497 protected void addDeprecatedOption(Option option) {
498 usedDeprecatedOptions.add(option);
499 }
500
501 public org.apache.commons.cli.Options getOptions() {
502 return options;
503 }
504
505 public Set<Option> getUsedDeprecatedOptions() {
506 return usedDeprecatedOptions;
507 }
508
509 public void displayHelp(String command, Consumer<String> pw) {
510 HelpFormatter formatter = new HelpFormatter();
511
512 int width = MessageUtils.getTerminalWidth();
513 if (width <= 0) {
514 width = HelpFormatter.DEFAULT_WIDTH;
515 }
516
517 pw.accept("");
518
519 StringWriter sw = new StringWriter();
520 PrintWriter pw2 = new PrintWriter(sw);
521 formatter.printHelp(
522 pw2,
523 width,
524 commandLineSyntax(command),
525 System.lineSeparator() + "Options:",
526 options,
527 HelpFormatter.DEFAULT_LEFT_PAD,
528 HelpFormatter.DEFAULT_DESC_PAD,
529 System.lineSeparator(),
530 false);
531 pw2.flush();
532 for (String s : sw.toString().split(System.lineSeparator())) {
533 pw.accept(s);
534 }
535 }
536
537 protected String commandLineSyntax(String command) {
538 return command + " [options] [goals]";
539 }
540 }
541 }