shellcommand-linecommandpicocli

Picocli: how to share options between parent command and subcommands


How to support shared options across all (or some) commands and subcommands? For example, if an end user provides --stacktrace anywhere in the command line arguments, any errors printed will include the stacktrace.

We can use a Mixin to define a boolean --stacktrace option, and include this Mixin in all (sub)commands, or use inheritance and let all commands subclass a class that defines a boolean --stacktrace option, but either way, that would define a separate boolean option for each command. Which option is true depends on where in the args the user put --stacktrace, so figuring out if --stacktrace was provided would mean iterating over all parent commands and seeing if any are true.

It would be nice to have a more convenient way to detect whether the user specified such a shared option.

(See also https://github.com/remkop/picocli/issues/580)


Solution

  • Update: with picocli 4.3 there are two ways to accomplish this:


    One way to accomplish this is to make the boolean field static:

    class Shared {
        @Option(names = "--stacktrace")
        static boolean stacktrace;
    }
    

    You would still need to either subclass or use a mixin to define this option in each command and subcommands:

    @Command(subcommands = {Subcommand1.class, Subcommand2.class /*, ...*/}
    class MyCommand {
        @Mixin
        Shared shared = new Shared();
    
        // ...
    }
    
    @Command(name = "subcommand1")
    class Subcommand1 {
        @Mixin
        Shared shared = new Shared();
    
        // ...
    }
    

    The nice thing about this design is that there is now a single place where the application can detect whether the end user specified --stacktrace: the static boolean field:

    public static void main(String... args) {
        assert Shared.stacktrace == false;
    
        MyCommand myCommand = new MyCommand();
        CommandLine.parseArgs(myCommand, "subcommand1", "--stacktrace");
    
        assert Shared.stacktrace == true;
    }