Skip to content

Latest commit

 

History

History

picocli-codegen

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 

Picocli Code Generation

picocli

1. About picocli-codegen

The picocli-codegen (Picocli Code Generation) module contains tools for generating configuration files and documentation for picocli-based applications.

This module also includes an annotation processor that can build a model from the picocli annotations at compile time rather than at runtime.

The annotation processor allows many of the tools to be invoked automatically as part of the build without configuration. If a tool does not have an annotation processor wrapper (yet), it can be invoked on the command line, and can be scripted to be invoked automatically as part of building your project.

As of picocli version 4.2, this module has three major use cases:

  • Compile time error checking. The annotation processor shows errors for invalid annotations and attributes immediately when you compile, instead of during testing at runtime, resulting in shorter feedback cycles.

  • GraalVm native images. To create a GraalVM native image for a picocli application, configuration is needed. The ReflectionConfigGenerator, ResourceConfigGenerator and DynamicProxyGenerator tools can generate reflect-config.json, resource-config.json and proxy-config.json configuration files for reflection, resources and dynamic proxies, respectively.

    The annotation processor embeds these three tools and generates the configuration files under META-INF/native-image/picocli-generated/$project during compilation, to be included in the application jar. By embedding these configuration files, your jar is instantly GraalVM-enabled. In most cases no further configuration is needed when generating a native image.

  • Generate Documentation. The ManPageGenerator tool can generate AsciiDoc documentation for picocli applications. AsciiDoc is a lightweight markup language that can easily can be converted to unix man pages, HTML and PDF with the wonderful asciidoctor tool.

2. Generate GraalVM Configurations with the Annotation Processor

To build a GraalVM native image for a picocli-based application, configuration files are needed to tell the GraalVM native-image generator tool which classes, methods and fields will be accessed reflectively, and which resources and dynamic proxies the application will use.

The picocli-codegen module has tools that generate these configuration files for a picocli-based application. The annotation processor in the picocli-codegen jar wraps these tools so they are executed automatically during compilation.

The annotation processor generates and updates GraalVM configuration files under META-INF/native-image/picocli-generated/$project during compilation, to be included in the application jar. This includes configuration files for reflection, resources and dynamic proxies. By embedding these configuration files, your jar is instantly GraalVM-enabled.

In most cases no further configuration is needed when generating a native image.

💡
It is recommended that you configure the $project location for your project to ensure the configuration files are in a unique location when jars are shaded into an uberjar, see processor options below.

2.1. Enabling the Annotation Processor

Since Java 6, annotation processing is part of the standard javac compiler, but many IDEs and build tools require something extra to enable annotation processing.

2.1.1. IDE

This page shows the steps to configure Eclipse and IntelliJ IDEA to enable annotation processing.

2.1.2. Maven

In Maven, use annotationProcessorPaths in the configuration of the maven-compiler-plugin. This requires maven-compiler-plugin plugin version 3.5 or higher.

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-compiler-plugin</artifactId>
  <!-- annotationProcessorPaths requires maven-compiler-plugin version 3.5 or higher -->
  <version>${maven-compiler-plugin-version}</version>
  <configuration>
    <annotationProcessorPaths>
      <path>
        <groupId>info.picocli</groupId>
        <artifactId>picocli-codegen</artifactId>
        <version>4.7.5</version>
      </path>
    </annotationProcessorPaths>
  </configuration>
</plugin>

An alternative that works with older versions of the maven-compiler-plugin is to specify the picocli-codegen module on the classpath as a provided dependency. This also prevents the picocli-codegen module from being included in the artifact the module produces as a transitive dependency.

<dependency>
  <groupId>info.picocli</groupId>
  <artifactId>picocli</artifactId>
  <version>4.7.5</version>
</dependency>

<dependency>
  <groupId>info.picocli</groupId>
  <artifactId>picocli-codegen</artifactId>
  <version>4.7.5</version>
  <scope>provided</scope>
</dependency>

See processor options below.

2.1.3. Gradle

Use the annotationProcessor path in Gradle 4.6 and higher:

dependencies {
    implementation 'info.picocli:picocli:4.7.5'
    annotationProcessor 'info.picocli:picocli-codegen:4.7.5'
}

For Gradle versions prior to 4.6, use compileOnly, to prevent the picocli-codegen jar from being a transitive dependency included in the artifact the module produces.

dependencies {
    implementation 'info.picocli:picocli:4.7.5'
    compileOnly 'info.picocli:picocli-codegen:4.7.5'
}

2.1.4. Kotlin Projects Using Maven

Add an execution of the kapt goal from kotlin-maven-plugin before compile:

<plugin>
    <artifactId>kotlin-maven-plugin</artifactId>
    <groupId>org.jetbrains.kotlin</groupId>
    <version>${kotlin.version}</version>
    <executions>
        <execution>
            <id>kapt</id>
            <goals>
                <goal>kapt</goal>
            </goals>
            <configuration>
                <sourceDirs>
                    <sourceDir>src/main/kotlin</sourceDir>
                    <sourceDir>src/main/java</sourceDir>
                </sourceDirs>
                <annotationProcessorPaths>
                    <annotationProcessorPath>
                        <groupId>info.picocli</groupId>
                        <artifactId>picocli-codegen</artifactId>
                        <version>4.7.5</version>
                    </annotationProcessorPath>
                </annotationProcessorPaths>
            </configuration>
        </execution>
        <execution>
            <id>compile</id>
            <!-- ... details omitted... -->
        </execution>
    </executions>
</plugin>

JetBrains' Kotlin repository has a complete sample project showing the use of Kotlin, Maven and Dagger.

2.1.5. Kotlin Projects Using Gradle

Kotlin projects should add the kotlin-kapt plugin to enable the Kotlin Annotation processing tool (kapt), then replace annotationProcessor with kapt:

apply plugin: 'kotlin-kapt' // required
dependencies {
    // ...
    kapt 'info.picocli:picocli-codegen:4.7.5'
}

And replace compileJava.options.compilerArgs with kapt.arguments:

kapt {
    arguments {
        arg("project", "${project.group}/${project.name}")
    }
}

2.2. Picocli Processor Options

The picocli annotation processor supports the options below.

  • project - output subdirectory

The generated files are written to META-INF/native-image/picocli-generated/${project}.

The project option can be omitted, but it is a good idea to specify the project option with a unique value for your project (e.g. ${project.groupId}/${project.artifactId}) if your jar may be shaded with other jars into an uberjar.

2.2.2. Other Options

  • verbose - (from picocli 4.3) print NOTE-level diagnostic messages to the console

  • other.resource.patterns - comma-separated list of regular expressions matching additional resources to include in the image

  • other.resource.bundles - comma-separated list of the base names of additional resource bundles to include in the image

  • other.proxy.interfaces - comma-separated list of the fully qualified class names of additional interfaces for which to generate proxy classes when building the image

  • disable.proxy.config - disable the DynamicProxyConfigGenerator tool so no proxy-config.json file is generated

  • disable.reflect.config - disable the ReflectionConfigGenerator tool so no reflect-config.json file is generated

  • disable.resource.config - disable the ResourceConfigGenerator tool so no resources-config.json file is generated

2.2.3. Javac

To pass an annotation processor option with javac, specify the -A command line option:

javac -Aproject=org.myorg.myproject/myapp -cp ...

The -A option lets you pass options to annotation processors. See the javac documentation for details.

2.2.4. Maven

To set an annotation processor option in Maven, you need to use the maven-compiler-plugin and configure the compilerArgs section.

<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-compiler-plugin</artifactId>
      <!-- annotationProcessorPaths requires maven-compiler-plugin version 3.5 or higher -->
      <version>${maven-compiler-plugin-version}</version>
      <configuration>
        <compilerArgs>
          <arg>-Aproject=${project.groupId}/${project.artifactId}</arg>
        </compilerArgs>
      </configuration>
    </plugin>
  </plugins>
</build>

2.3. Gradle Example

To set an annotation processor option in Gradle, add these options to the options.compilerArgs list in the compileJava block.

compileJava {
    // minimum 1.6
    sourceCompatibility = ${java-version}
    targetCompatibility = ${java-version}
    options.compilerArgs += ["-Aproject=${project.group}/${project.name}"]
}

See the Gradle documentation for details.

2.4. Gradle Example for Kotlin Projects

In Kotlin projects using Gradle, replace compileJava.options.compilerArgs with kapt.arguments:

kapt {
    arguments {
        arg("project", "${project.group}/${project.name}")
    }
}

3. Generate GraalVM Configurations Manually

The annotation processor is the recommended way to generate configuration files for GraalVM native images, but there may be cases where you want to generate these configuration files manually. For example, if your commands are written in Groovy, the picocli annotation processor will not be able to auto-generate configuration files for your commands, and you need to do some work to invoke the commands in your build.

The sections below give details on how to do this.

The picocli-codegen module contains the following tools to assist with AOT compilation to GraalVM native image builds:

  • ReflectionConfigGenerator

  • ResourceConfigGenerator

  • DynamicProxyConfigGenerator

The generated configuration files can be supplied to the native-image tool via command line options like -H:ReflectionConfigurationFiles=/path/to/reflect-config.json, or alternatively by placing them in a META-INF/native-image/ directory on the class path, for example, in a JAR file used in the image build. This directory (or any of its subdirectories) is searched for files with the names reflect-config.json, proxy-config.json and resource-config.json, which are then automatically included in the build. Not all of those files must be present. When multiple files with the same name are found, all of them are included.

See also the SubstrateVM configuration documentation.

3.1. ReflectionConfigGenerator

GraalVM has limited support for Java reflection and it needs to know ahead of time the reflectively accessed program elements.

ReflectionConfigGenerator generates a JSON String with the program elements that will be accessed reflectively in a picocli-based application, in order to compile this application ahead-of-time into a native executable with GraalVM.

The generated manual page for the ReflectionConfigGenerator tool is here.

The output of ReflectionConfigGenerator is intended to be passed to the -H:ReflectionConfigurationFiles=/path/to/reflect-config.json option of the native-image GraalVM utility, or placed in a META-INF/native-image/ subdirectory of the JAR.

This allows picocli-based applications to be compiled to a native image.

3.1.1. Generating Reflection Configuration During the Build

Note that the annotation processor does this automatically. The below is only of interest if you cannot use the annotation processor for some reason.

The --output option can be used to specify the path of the file to write the configuration to. When this option is omitted, the output is sent to standard out.

The ReflectionConfigGenerator tool accepts any number of fully qualified class names of command classes (classes with picocli annotations like @Command, @Option and @Parameters). The resulting configuration file will contain entries for the reflected elements of all specified classes.

Maven

For Maven, add an exec:java goal to generate a Graal reflection configuration file with the ReflectionConfigGenerator tool. This example uses the process-classes phase of the build, there are alternatives.

Note that the picocli-codegen module is only added as a dependency for the exec plugin, so it does not need to be added to the project dependencies.

<build>
  <plugins>
    <plugin>
      <groupId>org.codehaus.mojo</groupId>
      <artifactId>exec-maven-plugin</artifactId>
      <version>3.0.0</version>
      <executions>
        <execution>
          <id>generateGraalReflectionConfig</id>
          <phase>process-classes</phase>
          <goals>
            <goal>java</goal>
          </goals>
        </execution>
      </executions>
      <configuration>
        <includeProjectDependencies>true</includeProjectDependencies>
        <includePluginDependencies>true</includePluginDependencies>
        <mainClass>picocli.codegen.aot.graalvm.ReflectionConfigGenerator</mainClass>
        <arguments>
          <argument>--output=target/classes/META-INF/native-image/${project.groupId}/${project.artifactId}/reflect-config.json</argument>
          <argument>com.your.package.YourCommand1</argument>
          <argument>com.your.package.YourCommand2</argument>
        </arguments>
      </configuration>
      <dependencies>
        <dependency>
          <groupId>info.picocli</groupId>
          <artifactId>picocli-codegen</artifactId>
          <version>4.7.5</version>
          <type>jar</type>
        </dependency>
      </dependencies>
    </plugin>
  </plugins>
</build>
Gradle

For Gradle, add a custom configuration for the picocli-codegen module to your gradle.build. This allows us to add this module to the classpath of our custom task without adding it as a dependency to the "standard" build.

configurations {
    generateConfig
}
dependencies {
    implementation 'info.picocli:picocli:4.7.5'
    generateConfig 'info.picocli:picocli-codegen:4.7.5'
}

Then, add a custom task to run the ReflectionConfigGenerator tool. This example generates the file during the assemble lifecycle task, there are alternatives.

task(generateGraalReflectionConfig, dependsOn: 'classes', type: JavaExec) {
    main = 'picocli.codegen.aot.graalvm.ReflectionConfigGenerator'
    classpath = configurations.generateConfig + sourceSets.main.runtimeClasspath
    def outputFile = "${buildDir}/resources/main/META-INF/native-image/${project.group}/${project.name}/reflect-config.json"
    args = ["--output=$outputFile", 'com.your.package.YourCommand1', 'com.your.package.YourCommand2']
}
assemble.dependsOn generateGraalReflectionConfig

3.2. ResourceConfigGenerator

The GraalVM native-image builder by default will not integrate any of the classpath resources into the image it creates.

ResourceConfigGenerator generates a JSON String with the resource bundles and other classpath resources that should be included in the Substrate VM native image.

The generated manual page for the ResourceConfigGenerator tool is here.

The output of ResourceConfigGenerator is intended to be passed to the -H:ResourceConfigurationFiles=/path/to/resource-config.json option of the native-image GraalVM utility, or placed in a META-INF/native-image/ subdirectory of the JAR.

This allows picocli-based native image applications to access these resources.

3.2.1. Generating Resource Configuration During the Build

Note that the annotation processor does this automatically. The below is only of interest if you cannot use the annotation processor for some reason.

The --output option can be used to specify the path of the file to write the configuration to. When this option is omitted, the output is sent to standard out.

The ResourceConfigGenerator tool accepts any number of fully qualified class names of command classes (classes with picocli annotations like @Command, @Option and @Parameters). The resulting configuration file will contain entries for the resource bundles used in any of the specified commands or their subcommands.

The --bundle option can be used to specify the base name of additional resource bundle(s) to be included in the image.

The --pattern option can be used to specify Java regular expressions that match additional resource(s) to be included in the image.

Maven

For Maven, add an exec:java goal to generate a Graal resource configuration file with the ResourceConfigGenerator tool. This example uses the process-classes phase of the build, there are alternatives.

Note that the picocli-codegen module is only added as a dependency for the exec plugin, so it does not need to be added to the project dependencies.

<build>
  <plugins>
    <plugin>
      <groupId>org.codehaus.mojo</groupId>
      <artifactId>exec-maven-plugin</artifactId>
      <version>3.0.0</version>
      <executions>
        <execution>
          <id>generateGraalResourceConfig</id>
          <phase>process-classes</phase>
          <goals>
            <goal>java</goal>
          </goals>
        </execution>
      </executions>
      <configuration>
        <includeProjectDependencies>true</includeProjectDependencies>
        <includePluginDependencies>true</includePluginDependencies>
        <mainClass>picocli.codegen.aot.graalvm.ResourceConfigGenerator</mainClass>
        <arguments>
          <argument>--output=target/classes/META-INF/native-image/${project.groupId}/${project.artifactId}/resource-config.json</argument>
          <argument>com.your.package.YourCommand1</argument>
          <argument>com.your.package.YourCommand2</argument>
        </arguments>
      </configuration>
      <dependencies>
        <dependency>
          <groupId>info.picocli</groupId>
          <artifactId>picocli-codegen</artifactId>
          <version>4.7.5</version>
          <type>jar</type>
        </dependency>
      </dependencies>
    </plugin>
  </plugins>
</build>
Gradle

For Gradle, add a custom configuration for the picocli-codegen module to your gradle.build. This allows us to add this module to the classpath of our custom task without adding it as a dependency to the "standard" build.

configurations {
    generateConfig
}
dependencies {
    implementation 'info.picocli:picocli:4.7.5'
    generateConfig 'info.picocli:picocli-codegen:4.7.5'
}

Then, add a custom task to run the ResourceConfigGenerator tool. This example generates the file during the assemble lifecycle task, there are alternatives.

task(generateGraalResourceConfig, dependsOn: 'classes', type: JavaExec) {
    main = 'picocli.codegen.aot.graalvm.ResourceConfigGenerator'
    classpath = configurations.generateConfig + sourceSets.main.runtimeClasspath
    def outputFile = "${buildDir}/resources/main/META-INF/native-image/${project.group}/${project.name}/resource-config.json"
    args = ["--output=$outputFile", 'com.your.package.YourCommand1', 'com.your.package.YourCommand2']
}
assemble.dependsOn generateGraalResourceConfig

3.3. DynamicProxyConfigGenerator

Substrate VM doesn’t provide machinery for generating and interpreting bytecodes at run time. Therefore all dynamic proxy classes need to be generated at native image build time.

DynamicProxyConfigGenerator generates a JSON String with the fully qualified interface names for which dynamic proxy classes should be generated at native image build time.

The generated manual page for the DynamicProxyConfigGenerator tool is here.

The output of DynamicProxyConfigGenerator is intended to be passed to the -H:DynamicProxyConfigurationFiles=/path/to/proxy-config.json option of the native-image GraalVM utility, or placed in a META-INF/native-image/ subdirectory of the JAR.

This allows picocli-based native image applications that use @Command-annotated interfaces with @Option and @Parameters-annotated methods.

3.3.1. Generating Dynamic Proxy Configuration During the Build

Note that the annotation processor does this automatically. The below is only of interest if you cannot use the annotation processor for some reason.

The --output option can be used to specify the path of the file to write the configuration to. When this option is omitted, the output is sent to standard out.

The DynamicProxyConfigGenerator tool accepts any number of fully qualified class names of command classes (classes with picocli annotations like @Command, @Option and @Parameters). The resulting configuration file will contain entries for the resource bundles used in any of the specified commands or their subcommands.

The --interface option can be used to specify the fully qualified class names of additional interfaces to generate dynamic proxy classes for in the native image.

Maven

For Maven, add an exec:java goal to generate a Graal proxy configuration file with the DynamicProxyConfigGenerator tool. This example uses the process-classes phase of the build, there are alternatives.

Note that the picocli-codegen module is only added as a dependency for the exec plugin, so it does not need to be added to the project dependencies.

<build>
  <plugins>
    <plugin>
      <groupId>org.codehaus.mojo</groupId>
      <artifactId>exec-maven-plugin</artifactId>
      <version>3.0.0</version>
      <executions>
        <execution>
          <id>generateGraalDynamicProxyConfig</id>
          <phase>process-classes</phase>
          <goals>
            <goal>java</goal>
          </goals>
        </execution>
      </executions>
      <configuration>
        <includeProjectDependencies>true</includeProjectDependencies>
        <includePluginDependencies>true</includePluginDependencies>
        <mainClass>picocli.codegen.aot.graalvm.DynamicProxyConfigGenerator</mainClass>
        <arguments>
          <argument>--output=target/classes/META-INF/native-image/${project.groupId}/${project.artifactId}/proxy-config.json</argument>
          <argument>com.your.package.YourCommand1</argument>
          <argument>com.your.package.YourCommand2</argument>
        </arguments>
      </configuration>
      <dependencies>
        <dependency>
          <groupId>info.picocli</groupId>
          <artifactId>picocli-codegen</artifactId>
          <version>4.7.5</version>
          <type>jar</type>
        </dependency>
      </dependencies>
    </plugin>
  </plugins>
</build>
Gradle

For Gradle, add a custom configuration for the picocli-codegen module to your gradle.build. This allows us to add this module to the classpath of our custom task without adding it as a dependency to the "standard" build.

configurations {
    generateConfig
}
dependencies {
    implementation 'info.picocli:picocli:4.7.5'
    generateConfig 'info.picocli:picocli-codegen:4.7.5'
}

Then, add a custom task to run the DynamicProxyConfigGenerator tool. This example generates the file during the assemble lifecycle task, there are alternatives.

task(generateGraalDynamicProxyConfig, dependsOn: 'classes', type: JavaExec) {
    main = 'picocli.codegen.aot.graalvm.DynamicProxyConfigGenerator'
    classpath = configurations.generateConfig + sourceSets.main.runtimeClasspath
    def outputFile = "${buildDir}/resources/main/META-INF/native-image/${project.group}/${project.name}/proxy-config.json"
    args = ["--output=$outputFile", 'com.your.package.YourCommand1', 'com.your.package.YourCommand2']
}
assemble.dependsOn generateGraalDynamicProxyConfig

4. Generate Documentation

The picocli.codegen.docgen.manpage.ManPageGenerator tool introduced in picocli 4.2 generates AsciiDoc documentation using the manpage doctype and manpage document structure. The generated AsciiDoc files can be converted to HTML, PDF and unix man pages with the asciidoctor tool.

The generated manual page for the ManPageGenerator tool itself is here.

4.1. Gradle Example

Example gradle build.gradle snippet:

You will also need the AsciiDoctor Gradle Plugin.

dependencies {
    implementation "info.picocli:picocli:4.7.5"
    annotationProcessor "info.picocli:picocli-codegen:4.7.5"
}

mainClassName = "my.pkg.MyCommand"

task generateManpageAsciiDoc(type: JavaExec) {
    dependsOn(classes)
    group = "Documentation"
    description = "Generate AsciiDoc manpage"
    classpath(configurations.compileClasspath, configurations.annotationProcessor, sourceSets.main.runtimeClasspath)
    main 'picocli.codegen.docgen.manpage.ManPageGenerator'
    args mainClassName, "--outdir=${project.buildDir}/generated-picocli-docs", "-v" //, "--template-dir=src/docs/mantemplates"
}

apply plugin: 'org.asciidoctor.jvm.convert'
asciidoctor {
    dependsOn(generateManpageAsciiDoc)
    sourceDir = file("${project.buildDir}/generated-picocli-docs")
    outputDir = file("${project.buildDir}/docs")
    logDocuments = true
    outputOptions {
        backends = ['manpage', 'html5']
    }
}

The generateManpageAsciiDoc task generates $COMMANDNAME.adoc files with doctype manpage in build/generated-picocli-docs for each command and subcommand.

The asciidoctor task converts the generated $COMMANDNAME.adoc files in build/generated-picocli-docs to $COMMANDNAME.1 manpage files in build/docs/manpage/, and to $COMMANDNAME.html HTML files in build/docs/html5/.

See the example-gradle-project in the picocli-examples module for a full working example.

4.2. Maven Example

You will also need the AsciiDoctor Maven Plugin.

For Maven, add an exec:java goal to generate a Graal proxy configuration file with the ManPageGenerator tool. This example uses the process-classes phase of the build, there are alternatives.

Note that the picocli-codegen module is only added as a dependency for the exec plugin, so it does not need to be added to the project dependencies.

<build>
  <plugins>
    <plugin>
      <groupId>org.codehaus.mojo</groupId>
      <artifactId>exec-maven-plugin</artifactId>
      <version>3.0.0</version>
      <executions>
        <execution>
          <id>generateManPages</id>
          <phase>process-classes</phase>
          <goals>
            <goal>java</goal>
          </goals>
        </execution>
      </executions>
      <configuration>
        <includeProjectDependencies>true</includeProjectDependencies>
        <includePluginDependencies>true</includePluginDependencies>
        <mainClass>picocli.codegen.docgen.manpage.ManPageGenerator</mainClass>
        <arguments>
          <argument>--outdir=${project.build.directory}/generated-picocli-docs</argument>
          <argument>com.your.package.YourCommand1</argument>
          <argument>com.your.package.YourCommand2</argument>
        </arguments>
      </configuration>
      <dependencies>
        <dependency>
          <groupId>info.picocli</groupId>
          <artifactId>picocli-codegen</artifactId>
          <version>4.7.5</version>
          <type>jar</type>
        </dependency>
      </dependencies>
    </plugin>

    <plugin>
      <groupId>org.asciidoctor</groupId>
      <artifactId>asciidoctor-maven-plugin</artifactId>
      <version>2.1.0</version>
      <executions>
        <execution>
          <id>output-html</id>
          <phase>process-classes</phase>
          <goals>
            <goal>process-asciidoc</goal>
          </goals>
          <configuration>
            <sourceHighlighter>coderay</sourceHighlighter>
            <backend>html5</backend>
          </configuration>
        </execution>
        <execution>
          <id>output-manpage</id>
          <phase>process-classes</phase>
          <goals>
            <goal>process-asciidoc</goal>
          </goals>
          <configuration>
            <sourceHighlighter>coderay</sourceHighlighter>
            <backend>manpage</backend>
          </configuration>
        </execution>
      </executions>
      <configuration>
        <sourceDirectory>${project.build.directory}/generated-picocli-docs</sourceDirectory>
      </configuration>
    </plugin>
  </plugins>
</build>

See the example-maven-project in the picocli-examples module for a full working example.

4.3. Customizing with Templates

The generated man page is very similar to the online help generated by the command itself when a user specifies the --help option. You may want to add more details or extra sections to the man page.

To achieve this, the ManPageGenerator tool has an option to create an additional "template" file for each generated manpage AsciiDoc file.

4.3.1. Templates Concept

The generated manpage AsciiDoc files will be regenerated every build. We don’t want to edit these files because we would lose our changes every time the files are regenerated.

Instead, we will edit "template" files. Template files leverage AsciiDoctor’s include mechanism to import the contents of the generated manpage AsciiDoc files into a separate file.

The template page initially just contains a number of include::path/to/some.adoc[tag=xxx] statements. Each include statement imports part of a generated manpage AsciiDoc file. You can rearrange these includes and add text after each include to customize the resulting man page.

Once we created the template files, we will need to tell the asciidoctor tool to take these template files as input instead of the generated manpage AsciiDoc files.

The resulting man page will be a mixture of generated and manually edited text.

The ManPageGenerator tool can generate template files, but template files should not be generated multiple times. They are intended to be generated only once, and afterwards be manually updated and maintained. Changes in the generated manpage AsciiDoc files will then be reflected in the final output via the includes, without impacting the additions or modifications you made to the template files.

4.3.2. Creating Template Files

To create template pages, specify the --template-dir option when invoking the picocli.codegen.docgen.manpage.ManPageGenerator tool. For example:

java -cp $CLASSPATH picocli.codegen.docgen.manpage.ManPageGenerator \
     -v --outdir=${project.buildDir}/generated/docs \
     --template-dir=src/docs/man-templates \
     my.pkg.MyCommand

This generates AsciiDoc files with the 'manpage' doctype in build/generated/docs, and template files in src/docs/man-templates.

🔥
Do this only once, then remove the --template-dir option, so that subsequent ManPageGenerator invocations will only update the generated manpage AsciiDoc files and will not overwrite the template files.
🔥
If the ManPageGenerator tool detects an existing template file, it will abort with an error (and exit code 4). The existing template will not be overwritten unless the --force option is specified.

We can now edit the files in src/docs/man-templates and tell the asciidoctor tool to generate HTML and man page files in troff format from the files in src/docs/man-templates.

4.3.3. Using Template Files

In its simplest form a template file can include the full generated page with the picocli-generated-full-manpage tag. This allows us to add some sections to the end of the page. For example:

// src/docs/man-templates/xxx.adoc
:includedir: ../../../build/generated/docs
include::{includedir}/xxx.adoc[tag=picocli-generated-full-manpage]

== Authors
My, myself and I.

It is also possible to include the picocli-generated sections individually, so that the generated sections can be customized with additional text that follows the generated text for the section. For example:

// src/docs/man-templates/yyy.adoc
:includedir: ../../../build/generated/docs
include::{includedir}/yyy.adoc[tag=picocli-generated-man-section-header]

include::{includedir}/yyy.adoc[tag=picocli-generated-man-section-name]

This is a very good tool that will serve you well.

include::{includedir}/yyy.adoc[tag=picocli-generated-man-section-synopsis]

*mycommand* [-hVv] [a=VERY] [--different=synopsis]

include::{includedir}/yyy.adoc[tag=picocli-generated-man-section-description]

Here is one additional description line.

Here is another additional description line.

include::{includedir}/yyy.adoc[tag=picocli-generated-man-section-options]

include::{includedir}/yyy.adoc[tag=picocli-generated-man-section-arguments]

include::{includedir}/yyy.adoc[tag=picocli-generated-man-section-commands]

include::{includedir}/yyy.adoc[tag=picocli-generated-man-section-exit-status]

include::{includedir}/yyy.adoc[tag=picocli-generated-man-section-footer]

== More Examples

[%hardbreaks]
Here are some extra examples:
abc def ghi jkl mno pq
abc def ghi jkl mno pq
abc def ghi jkl mno pq

== Authors
All of us.

== Copyright
Just hand it out, it's fine.

Then, when invoking the asciidoctor tool, specify the directory containing the template files as the source directory. For example:

# generate man pages in troff format in build/docs/manpage
asciidoctor --backend=manpage --source-dir=src/docs/man-templates --destination-dir=build/docs *.adoc

# generate man pages in HTML format in build/docs/html5
asciidoctor --backend=html5 --source-dir=src/docs/man-templates --destination-dir=build/docs *.adoc