picocli icon indicating copy to clipboard operation
picocli copied to clipboard

DOC: improve example/docs getting version from jar manifest

Open remkop opened this issue 2 years ago • 3 comments

As suggested by @rileynull in https://github.com/remkop/picocli/issues/236#issuecomment-1105308193, the example showing how to get version information from the manifest may be overly convoluted.

TODO: Experiment with this and update the docs if this works as expected.

(Potential example for the manual:

class ManifestBasedVersionProviderWithVariables implements IVersionProvider {
    public String[] getVersion() {
        String version = getClass().getPackage().getImplementationVersion();
        return new String[] { "${COMMAND-FULL-NAME} version " + version };
    }
}

remkop avatar Apr 22 '22 00:04 remkop

Note that the Class.getPackage method returns a Package object that is populated with information from the manifest (the /META-INF/MANIFEST.MF file in the jar file). If the Class that is being inspected is not in a jar that has a /META-INF/MANIFEST.MF file, the getPackage method returns null.

This should be mentioned in the documentation.

remkop avatar Aug 13 '22 09:08 remkop

If the Class that is being inspected is not in a jar that has a /META-INF/MANIFEST.MF file, the getPackage method returns null.

And also if that MANIFEST.MF exists, but does not have the Implementation-Version property. Which happens to be the default for Maven's Archiver: https://maven.apache.org/shared/maven-archiver/index.html#class_manifest

klausbrunner avatar Jan 11 '23 11:01 klausbrunner

Some observations from my end:

  1. When I still had a non-modular Java project, I could read my project title and version from the manifest file using the code @remkop mentioned above:

    String title = getClass().getPackage().getImplementationTitle();
    String version = getClass().getPackage().getImplementationVersion();
    
  2. The above approach failed when I wrapped all my code in a Java module. Both getImplementation...() calls returned null, and I could not find a way around it. Following the example from VersionProvider2.java, I could again read my project version (if I specified my implementation title instead of "picocli", of course).

  3. I just found an alternative to VersionProvider2.java that is much shorter and seems to work well for modular Java code:

    • Set the module version using the --module-version parameter of javac. With Gradle, a (Kotlin DSL) task definition like the following does the job:

      tasks {
          compileJava {
              options.javaModuleVersion.set("${project.version}")
          }
      }
      
    • In the IVersionProvider implementation, read the module version from the ModuleDescriptor:

      String version = "";
      
      ModuleDescriptor moduleDescriptor = getClass().getModule().getDescriptor();
      
      if (moduleDescriptor != null) {
          Optional<ModuleDescriptor.Version> optional = moduleDescriptor.version();
      
          if (optional.isPresent()) {
              version = optional.get().toString();
          }
      }
      

I'm not sure if approach 1 really can't be adapted for modular code. Neither can I ensure approach 3 always works. But hopefully, someone else can step in and explain if approach 3 is a viable alternative to VersionProvider2.java for modular code.

mauritssilvis avatar Mar 30 '23 22:03 mauritssilvis