-
Notifications
You must be signed in to change notification settings - Fork 426
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
When compiling to a native image with Graal, no colors are shown #557
Comments
What do you get when you invoke the native image with system property
I suspect there is a problem with picocli's logic to detect that it's safe to switch on ANSI colors. What is the value of environment variables |
Indeed, when calling the binary with The output of $ echo $TERM
xterm-256color
$ echo $OSTYPE
darwin17.5.0 So this is a Mac I'm developing on. Note that I'm also building the binary without Graals This is my Graal config (I'm using Palantir's Graal Gradle Plugin) graal {
mainClass 'de.meinestadt.mock.Cli'
outputName project.name
option "-H:ReflectionConfigurationFiles=${project.buildDir}/cli-reflect.json"
option '-H:+ReportUnsupportedElementsAtRuntime'
}
nativeImage.dependsOn 'generateGraalReflectionConfig', 'generateCompletionScript' Thank you for your help! |
Ok, I suspect that this is caused by picocli's check whether the console is a TTY. Picocli uses reflection to detect this, and this reflection is not in the configuration. Unsure why ANSI colors worked for me when I tested... Can you try adding the below snippet to
|
If we can confirm that colors are shown when the above snippet is included in the |
@helpermethod, do you think you'll be able to test the above JSON snippet this week? I'm thinking to do another release this weekend and would be great if you could confirm that this fixes the issue. |
Hi @remkop ! Sadly, this doesn't seem to make a difference. Added the entry but still no colors. |
@helpermethod Can you please try again with with the latest picocli built from master? |
I suspect that The debug tracing now in master will confirm this. You can also confirm by printing the result of calling By the way, can you give some version information on the operating system and Java version/vendor? |
@remkop This is the 11:22 $ ./mock-cli -Dpicocli.trace=DEBUG
[picocli DEBUG] Creating CommandSpec for object of class de.meinestadt.mock.Cli with factory picocli.CommandLine$DefaultFactory
[picocli DEBUG] Creating CommandSpec for object of class de.meinestadt.mock.subcommand.Create with factory picocli.CommandLine$DefaultFactory
[picocli DEBUG] Creating CommandSpec for object of class java.lang.reflect.Method with factory picocli.CommandLine$DefaultFactory
[picocli DEBUG] Using method public void de.meinestadt.mock.subcommand.Create.config(java.lang.String,java.util.List,java.lang.String) as command
[picocli DEBUG] Creating CommandSpec for object of class java.lang.reflect.Method with factory picocli.CommandLine$DefaultFactory
[picocli DEBUG] Using method public void de.meinestadt.mock.subcommand.Create.mapping(java.lang.String,java.lang.String,java.util.Map) as command
[picocli DEBUG] Creating CommandSpec for object of class de.meinestadt.mock.subcommand.List with factory picocli.CommandLine$DefaultFactory
[picocli DEBUG] Creating CommandSpec for object of class java.lang.reflect.Method with factory picocli.CommandLine$DefaultFactory
[picocli DEBUG] Using method public void de.meinestadt.mock.subcommand.List.configs() as command
[picocli DEBUG] Creating CommandSpec for object of class java.lang.reflect.Method with factory picocli.CommandLine$DefaultFactory
[picocli DEBUG] Using method public void de.meinestadt.mock.subcommand.List.mappings() as command
[picocli DEBUG] Creating CommandSpec for object of class de.meinestadt.mock.subcommand.Show with factory picocli.CommandLine$DefaultFactory
[picocli DEBUG] Creating CommandSpec for object of class java.lang.reflect.Method with factory picocli.CommandLine$DefaultFactory
[picocli DEBUG] Using method public void de.meinestadt.mock.subcommand.Show.config(java.lang.String) as command
[picocli DEBUG] Creating CommandSpec for object of class picocli.CommandLine$AutoHelpMixin with factory picocli.CommandLine$DefaultFactory
[picocli INFO] Parsing 0 command line args []
[picocli DEBUG] Parser configuration: posixClusteredShortOptionsAllowed=true, stopAtPositional=false, stopAtUnmatched=false, separator=null, overwrittenOptionsAllowed=false, unmatchedArgumentsAllowed=false, expandAtFiles=true, atFileCommentChar=#, endOfOptionsDelimiter=--, limitSplit=false, aritySatisfiedByAttachedOptionParam=false, toggleBooleanFlags=true, unmatchedOptionsArePositionalParams=false, collectErrors=false,caseInsensitiveEnumValuesAllowed=true, trimQuotes=false, splitQuotedStrings=false
[picocli DEBUG] Set initial value for field boolean picocli.CommandLine$AutoHelpMixin.helpRequested of type boolean to false.
[picocli DEBUG] Set initial value for field boolean picocli.CommandLine$AutoHelpMixin.versionRequested of type boolean to false.
[picocli DEBUG] Initializing de.meinestadt.mock.Cli: 2 options, 0 positional parameters, 0 required, 3 subcommands.
[picocli DEBUG] Creating CommandSpec for object of class de.meinestadt.mock.Cli with factory picocli.CommandLine$DefaultFactory
[picocli DEBUG] Creating CommandSpec for object of class de.meinestadt.mock.subcommand.Create with factory picocli.CommandLine$DefaultFactory
[picocli DEBUG] Creating CommandSpec for object of class java.lang.reflect.Method with factory picocli.CommandLine$DefaultFactory
[picocli DEBUG] Using method public void de.meinestadt.mock.subcommand.Create.config(java.lang.String,java.util.List,java.lang.String) as command
[picocli DEBUG] Creating CommandSpec for object of class java.lang.reflect.Method with factory picocli.CommandLine$DefaultFactory
[picocli DEBUG] Using method public void de.meinestadt.mock.subcommand.Create.mapping(java.lang.String,java.lang.String,java.util.Map) as command
[picocli DEBUG] Creating CommandSpec for object of class de.meinestadt.mock.subcommand.List with factory picocli.CommandLine$DefaultFactory
[picocli DEBUG] Creating CommandSpec for object of class java.lang.reflect.Method with factory picocli.CommandLine$DefaultFactory
[picocli DEBUG] Using method public void de.meinestadt.mock.subcommand.List.configs() as command
[picocli DEBUG] Creating CommandSpec for object of class java.lang.reflect.Method with factory picocli.CommandLine$DefaultFactory
[picocli DEBUG] Using method public void de.meinestadt.mock.subcommand.List.mappings() as command
[picocli DEBUG] Creating CommandSpec for object of class de.meinestadt.mock.subcommand.Show with factory picocli.CommandLine$DefaultFactory
[picocli DEBUG] Creating CommandSpec for object of class java.lang.reflect.Method with factory picocli.CommandLine$DefaultFactory
[picocli DEBUG] Using method public void de.meinestadt.mock.subcommand.Show.config(java.lang.String) as command
[picocli DEBUG] Creating CommandSpec for object of class picocli.CommandLine$AutoHelpMixin with factory picocli.CommandLine$DefaultFactory |
Did that show the usage help?
|
Yes it did. 14:16 $ ./mock-cli -Dpicocli.trace=DEBUG --help
[picocli DEBUG] Creating CommandSpec for object of class de.meinestadt.mock.Cli with factory picocli.CommandLine$DefaultFactory
[picocli DEBUG] Creating CommandSpec for object of class de.meinestadt.mock.subcommand.Create with factory picocli.CommandLine$DefaultFactory
[picocli DEBUG] Creating CommandSpec for object of class java.lang.reflect.Method with factory picocli.CommandLine$DefaultFactory
[picocli DEBUG] Using method public void de.meinestadt.mock.subcommand.Create.config(java.lang.String,java.util.List,java.lang.String) as command
[picocli DEBUG] Creating CommandSpec for object of class java.lang.reflect.Method with factory picocli.CommandLine$DefaultFactory
[picocli DEBUG] Using method public void de.meinestadt.mock.subcommand.Create.mapping(java.lang.String,java.lang.String,java.util.Map) as command
[picocli DEBUG] Creating CommandSpec for object of class de.meinestadt.mock.subcommand.List with factory picocli.CommandLine$DefaultFactory
[picocli DEBUG] Creating CommandSpec for object of class java.lang.reflect.Method with factory picocli.CommandLine$DefaultFactory
[picocli DEBUG] Using method public void de.meinestadt.mock.subcommand.List.configs() as command
[picocli DEBUG] Creating CommandSpec for object of class java.lang.reflect.Method with factory picocli.CommandLine$DefaultFactory
[picocli DEBUG] Using method public void de.meinestadt.mock.subcommand.List.mappings() as command
[picocli DEBUG] Creating CommandSpec for object of class de.meinestadt.mock.subcommand.Show with factory picocli.CommandLine$DefaultFactory
[picocli DEBUG] Creating CommandSpec for object of class java.lang.reflect.Method with factory picocli.CommandLine$DefaultFactory
[picocli DEBUG] Using method public void de.meinestadt.mock.subcommand.Show.config(java.lang.String) as command
[picocli DEBUG] Creating CommandSpec for object of class picocli.CommandLine$AutoHelpMixin with factory picocli.CommandLine$DefaultFactory
[picocli INFO] Parsing 1 command line args [--help]
[picocli DEBUG] Parser configuration: posixClusteredShortOptionsAllowed=true, stopAtPositional=false, stopAtUnmatched=false, separator=null, overwrittenOptionsAllowed=false, unmatchedArgumentsAllowed=false, expandAtFiles=true, atFileCommentChar=#, endOfOptionsDelimiter=--, limitSplit=false, aritySatisfiedByAttachedOptionParam=false, toggleBooleanFlags=true, unmatchedOptionsArePositionalParams=false, collectErrors=false,caseInsensitiveEnumValuesAllowed=true, trimQuotes=false, splitQuotedStrings=false
[picocli DEBUG] Set initial value for field boolean picocli.CommandLine$AutoHelpMixin.helpRequested of type boolean to false.
[picocli DEBUG] Set initial value for field boolean picocli.CommandLine$AutoHelpMixin.versionRequested of type boolean to false.
[picocli DEBUG] Initializing de.meinestadt.mock.Cli: 2 options, 0 positional parameters, 0 required, 3 subcommands.
[picocli DEBUG] Processing argument '--help'. Remainder=[]
[picocli DEBUG] '--help' cannot be separated into <option>=<option-parameter>
[picocli DEBUG] Found option named '--help': field boolean picocli.CommandLine$AutoHelpMixin.helpRequested, arity=0
[picocli INFO] field boolean picocli.CommandLine$AutoHelpMixin.helpRequested has 'usageHelp' annotation: not validating required fields
[picocli INFO] Setting field boolean picocli.CommandLine$AutoHelpMixin.helpRequested to 'true' (was 'false') for option --help
Usage: mock-cli [-hV] [COMMAND]
-h, --help Show this help message and exit.
-V, --version Print version information and exit.
Commands:
create creates a configuration or mapping
list lists available configurations or mappings
show displays a configuration or mapping |
Hmm... my last commit to master should introduce a DEBUG message starting with “Checking if ANSI possible: isTTY=...” but I don’t see that in the output. Did you use picocli 3.8 or the snapshot build from master? What I’m trying to confirm is whether |
Yes, used the SNAPSHOT version but with no difference. I've uploaded the project for analysis to GH |
What I've tried so far:
After that didn't work out, I've install the 4.0.0-SNAPSHOT into my local Maven repository and let my project depend on the SNAPSHOT version. |
Picocli 4.0.0-SNAPSHOT has some additional debug tracing but if this is proving cumbersome we can do it differently. I would like to find out if
|
FYI: I noticed the tracing I added on whether ANSI is available was not always shown. Probably because it was being logged from a static initializer method. I've made some improvements to the tracing (#560). |
Thank you! Will test out your changes on Monday (don't have my laptop at home right now, sry). |
The output based on your latest changes 09:19 $ ./mock-cli -Dpicocli.trace=DEBUG
[picocli DEBUG] Creating CommandSpec for object of class de.meinestadt.mock.Cli with factory picocli.CommandLine$DefaultFactory
[picocli DEBUG] Creating CommandSpec for object of class de.meinestadt.mock.subcommand.Create with factory picocli.CommandLine$DefaultFactory
[picocli DEBUG] Creating CommandSpec for object of class java.lang.reflect.Method with factory picocli.CommandLine$DefaultFactory
[picocli DEBUG] Using method public void de.meinestadt.mock.subcommand.Create.config(java.lang.String,java.util.List,java.lang.String) as command
[picocli DEBUG] Creating CommandSpec for object of class java.lang.reflect.Method with factory picocli.CommandLine$DefaultFactory
[picocli DEBUG] Using method public void de.meinestadt.mock.subcommand.Create.mapping(java.lang.String,java.lang.String,java.util.Map) as command
[picocli DEBUG] Creating CommandSpec for object of class de.meinestadt.mock.subcommand.List with factory picocli.CommandLine$DefaultFactory
[picocli DEBUG] Creating CommandSpec for object of class java.lang.reflect.Method with factory picocli.CommandLine$DefaultFactory
[picocli DEBUG] Using method public void de.meinestadt.mock.subcommand.List.configs() as command
[picocli DEBUG] Creating CommandSpec for object of class java.lang.reflect.Method with factory picocli.CommandLine$DefaultFactory
[picocli DEBUG] Using method public void de.meinestadt.mock.subcommand.List.mappings() as command
[picocli DEBUG] Creating CommandSpec for object of class de.meinestadt.mock.subcommand.Show with factory picocli.CommandLine$DefaultFactory
[picocli DEBUG] Creating CommandSpec for object of class java.lang.reflect.Method with factory picocli.CommandLine$DefaultFactory
[picocli DEBUG] Using method public void de.meinestadt.mock.subcommand.Show.config(java.lang.String) as command
[picocli DEBUG] Creating CommandSpec for object of class picocli.CommandLine$AutoHelpMixin with factory picocli.CommandLine$DefaultFactory
[picocli INFO] Picocli version: 4.0.0-SNAPSHOT, JVM: 1.8.0_192 (Oracle Corporation Substrate VM null), OS: Mac OS X 10.13.4 x86_64
[picocli INFO] Parsing 0 command line args []
[picocli DEBUG] Parser configuration: posixClusteredShortOptionsAllowed=true, stopAtPositional=false, stopAtUnmatched=false, separator=null, overwrittenOptionsAllowed=false, unmatchedArgumentsAllowed=false, expandAtFiles=true, atFileCommentChar=#, endOfOptionsDelimiter=--, limitSplit=false, aritySatisfiedByAttachedOptionParam=false, toggleBooleanFlags=true, unmatchedOptionsArePositionalParams=false, collectErrors=false,caseInsensitiveEnumValuesAllowed=true, trimQuotes=false, splitQuotedStrings=false
[picocli DEBUG] (ANSI is disabled by default: TTY=false, isXTERM=true, hasOSTYPE=false, isWindows=false, JansiConsoleInstalled=false)
[picocli DEBUG] Set initial value for field boolean picocli.CommandLine$AutoHelpMixin.helpRequested of type boolean to false.
[picocli DEBUG] Set initial value for field boolean picocli.CommandLine$AutoHelpMixin.versionRequested of type boolean to false.
[picocli DEBUG] Initializing de.meinestadt.mock.Cli: 2 options, 0 positional parameters, 0 required, 3 subcommands.
[picocli DEBUG] Creating CommandSpec for object of class de.meinestadt.mock.Cli with factory picocli.CommandLine$DefaultFactory
[picocli DEBUG] Creating CommandSpec for object of class de.meinestadt.mock.subcommand.Create with factory picocli.CommandLine$DefaultFactory
[picocli DEBUG] Creating CommandSpec for object of class java.lang.reflect.Method with factory picocli.CommandLine$DefaultFactory
[picocli DEBUG] Using method public void de.meinestadt.mock.subcommand.Create.config(java.lang.String,java.util.List,java.lang.String) as command
[picocli DEBUG] Creating CommandSpec for object of class java.lang.reflect.Method with factory picocli.CommandLine$DefaultFactory
[picocli DEBUG] Using method public void de.meinestadt.mock.subcommand.Create.mapping(java.lang.String,java.lang.String,java.util.Map) as command
[picocli DEBUG] Creating CommandSpec for object of class de.meinestadt.mock.subcommand.List with factory picocli.CommandLine$DefaultFactory
[picocli DEBUG] Creating CommandSpec for object of class java.lang.reflect.Method with factory picocli.CommandLine$DefaultFactory
[picocli DEBUG] Using method public void de.meinestadt.mock.subcommand.List.configs() as command
[picocli DEBUG] Creating CommandSpec for object of class java.lang.reflect.Method with factory picocli.CommandLine$DefaultFactory
[picocli DEBUG] Using method public void de.meinestadt.mock.subcommand.List.mappings() as command
[picocli DEBUG] Creating CommandSpec for object of class de.meinestadt.mock.subcommand.Show with factory picocli.CommandLine$DefaultFactory
[picocli DEBUG] Creating CommandSpec for object of class java.lang.reflect.Method with factory picocli.CommandLine$DefaultFactory
[picocli DEBUG] Using method public void de.meinestadt.mock.subcommand.Show.config(java.lang.String) as command
[picocli DEBUG] Creating CommandSpec for object of class picocli.CommandLine$AutoHelpMixin with factory picocli.CommandLine$DefaultFactory
Usage: mock-cli [-hV] [COMMAND]
-h, --help Show this help message and exit.
-V, --version Print version information and exit.
Commands:
create creates a configuration or mapping
list lists available configurations or mappings
show displays a configuration or mapping |
Relevant line:
|
Right. I'm 99% sure that this means that If The question is why
|
That doesn't seem to be the problem, 13:52 $ ./mock-cli
console: java.io.Console@108e8f140
Usage: mock-cli [-hV] [COMMAND]
-h, --help Show this help message and exit.
-V, --version Print version information and exit.
Commands:
create creates a configuration or mapping
list lists available configurations or mappings
show displays a configuration or mapping |
Really? That’s unexpected!
|
This is the output 17:32 $ ./mock-cli
JVM: 1.8.0_192 (Oracle Corporation Substrate VM null), OS: Mac OS X 10.13.4 x86_64
System.console() = java.io.Console@107b90458
System.class.getDeclaredMethod("console").invoke(null) = java.io.Console@107b90458
Usage: mock-cli [-hV] [COMMAND]
-h, --help Show this help message and exit.
-V, --version Print version information and exit.
Commands:
create creates a configuration or mapping
list lists available configurations or mappings
show displays a configuration or mapping Produced by the following Java snippet @Override
public void run() {
System.out.printf("JVM: %s (%s %s %s), OS: %s %s %s%n",
System.getProperty("java.version"), System.getProperty("java.vendor"),
System.getProperty("java.vm.name"), System.getProperty("java.vm.version"),
System.getProperty("os.name"), System.getProperty("os.version"),
System.getProperty("os.arch"));
out.println("System.console() = " + System.console());
try {
out.println("System.class.getDeclaredMethod(\"console\").invoke(null) = " + System.class.getDeclaredMethod("console").invoke(null));
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
e.printStackTrace();
}
new CommandLine(this).usage(out);
} So both invocations seem to return a non-null object. |
Thank you very much for sticking with it to help me figure this out! I made a change to picocli master: it no longer reflects on Can you try building picocli from master again to see if it shows colors now? We can also test my hypothesis directly in your program with this code: public class Cli implements Runnable {
static final Boolean TTY = calcTTY();
static final boolean calcTTY() {
try {
TTY = System.class.getDeclaredMethod("console").invoke(null) == null;
} catch (Exception ex) {
ex.printStackTrace();
}
}
@Override
public void run() {
System.out.printf("JVM: %s (%s %s %s), OS: %s %s %s%n",
System.getProperty("java.version"), System.getProperty("java.vendor"),
System.getProperty("java.vm.name"), System.getProperty("java.vm.version"),
System.getProperty("os.name"), System.getProperty("os.version"),
System.getProperty("os.arch"));
out.println("TTY in static initializer = " + TTY);
out.println("System.console() = " + System.console());
try {
out.println("System.class.getDeclaredMethod(\"console\").invoke(null) = " + System.class.getDeclaredMethod("console").invoke(null));
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
e.printStackTrace();
}
new CommandLine(this).usage(out);
}
} But if possible please run it after building picocli from the latest master sources. |
Somehow it now works! 09:11 $ ./mock-cli
JVM: 1.8.0_192 (Oracle Corporation Substrate VM null), OS: Mac OS X 10.13.4 x86_64
TTY in static initializer = true
System.console() = java.io.Console@1066907d8
System.class.getDeclaredMethod("console").invoke(null) = java.io.Console@1066907d8
Usage: mock-cli [-hV] [COMMAND]
-h, --help Show this help message and exit.
-V, --version Print version information and exit.
Commands:
create creates a configuration or mapping
list lists available configurations or mappings
show displays a configuration or mapping |
Excellent! This is after rebuilding picocli from the latest sources in master? |
If my latest commit on master fixed the issue, I will do a 3.8.2 release for this. |
Yes, this was after using the latest master! Great work, thank you very much! |
I released v3.8.2 that includes this fix. |
I've managed to compile my Java-based CLI with Graal following the steps described in
https://picocli.info/picocli-on-graalvm.html
It works great, except for one thing: when the help is shown, no colors are displayed.
This is the same output produced by the JAR.
The text was updated successfully, but these errors were encountered: