package picocli.codegen.aot.graalvm; import picocli.CommandLine; import picocli.CommandLine.Command; import picocli.CommandLine.Mixin; import picocli.CommandLine.Option; import picocli.CommandLine.Parameters; import picocli.codegen.aot.graalvm.ReflectionConfigGenerator.ReflectedClass; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Callable; public class JniConfigGenerator { @Command(name = "gen-jni-config", showAtFileInUsageHelp = true, description = {"Generates a JSON file with the program elements that will be " + "accessed reflectively from native code.", "The generated JSON file can be passed to the `-H:JNIConfigurationFiles=/path/to/jni-config.json` " + "option of the `native-image` GraalVM utility.", "See https://github.com/oracle/graal/blob/master/substratevm/JNI.md"}, exitCodeListHeading = "%nExit Codes (if enabled with `--exit`)%n", exitCodeList = { "0:Successful program execution.", "1:A runtime exception occurred while generating man pages.", "2:Usage error: user input for the command was incorrect, " + "e.g., the wrong number of arguments, a bad flag, " + "a bad syntax in a parameter, etc." }, footerHeading = "%nExample%n", footer = { " java -cp \"myapp.jar;picocli-4.6.1.jar;picocli-codegen-4.6.1.jar\" " + "picocli.codegen.aot.graalvm.JniConfigGenerator my.pkg.MyClass" }, mixinStandardHelpOptions = true, sortOptions = false, version = "picocli-codegen ${COMMAND-NAME} " + CommandLine.VERSION) private static class App implements Callable { @Parameters(arity = "1..*", description = "One or more classes to generate a GraalVM JNI configuration for.") Class[] classes = new Class[0]; @Mixin OutputFileMixin outputFile = new OutputFileMixin(); @Option(names = "--exit", negatable = true, description = "Specify `--exit` if you want the application to call `System.exit` when finished. " + "By default, `System.exit` is not called.") boolean exit; public Integer call() throws Exception { String result = generateJniConfig(classes).toString(); outputFile.write(result); return 0; } } /** * Runs this class as a standalone application, printing the resulting JSON String to a file or to {@code System.out}. * @param args one or more fully qualified class names of {@code @Command}-annotated classes. */ public static void main(String... args) { App app = new App(); int exitCode = new CommandLine(app).execute(args); if (app.exit) { System.exit(exitCode); } } private static StringBuilder generateJniConfig(Class[] classes) { List visited = visit(classes); StringBuilder result = new StringBuilder(1024); String prefix = String.format("[%n"); String suffix = String.format("%n]%n"); for (ReflectedClass cls : visited) { result.append(prefix).append(cls); prefix = String.format(",%n"); } return result.append(suffix); } private static List visit(Class[] classes) { List result = new ArrayList(); for (Class cls : classes) { visit(cls, result); } return result; } private static void visit(Class cls, List result) { ReflectedClass reflected = new ReflectedClass(cls.getName()); result.add(reflected); for (Method method : cls.getDeclaredMethods()) { reflected.addMethod(method); } for (Field field : cls.getDeclaredFields()) { reflected.addField(field); } for (Class inner : cls.getDeclaredClasses()) { visit(inner, result); } } }