34

I've looked this up on Google, but there doesn't seem to be any documentation on the Gradle site, or even people discussing this in forums.

I have Gradle installed on my Mac (10.8.2, ML) and am building a custom build.gradle script. When I call println(), I would like to make the output colored (like errors in red, info in green, etc). How do I do this in my gradle build script?

Here's an example of code I have so far:

def proc = "echo `DATE`".execute()
proc.in.eachLine {line -> println line}
proc.err.eachLine {line -> println 'ERROR: ' + line}

On this gradle forum, they talk about various styles like normal, header, userinput, identifier, description, progressstatus, failure, info, and error, as part of the StyledTextOutput class. It looks like this is an internal class. Is there a simple way to tap into the color printing powers of Gradle/Groovy without importing a lot of packages?

doelleri
  • 19,232
  • 5
  • 61
  • 65
tralston
  • 2,825
  • 3
  • 19
  • 22

6 Answers6

33

Found the answer! According to this gradle forum post, there's no public interface for coloring the output of the logger. You are free to use the internal classes, but those may change in future versions. In the gradle script, put at the top:

Older Gradle:

import org.gradle.logging.StyledTextOutput;
import org.gradle.logging.StyledTextOutputFactory;
import static org.gradle.logging.StyledTextOutput.Style;

Gradle 3.3+:

import org.gradle.internal.logging.text.StyledTextOutput;
import org.gradle.internal.logging.text.StyledTextOutputFactory;
import static org.gradle.internal.logging.text.StyledTextOutput.Style;

(I still haven't figured out how to move this to the init.gradle file.) Then shortly after, define

def out = services.get(StyledTextOutputFactory).create("blah")

I'm still not sure what needs to be in the create method's string (it doesn't seem to affect anything yet). Then later in your task,

out.withStyle(Style.Info).println('colored text')

This should work with all the categories: normal, header, userinput, identifier, description, progressstatus, failure, info, and error. An additional step if you want to customize the color of each category, add the following to an init.gradle file in your ~/.gradle directory (or other options):

System.setProperty('org.gradle.color.error', 'RED')

and then replace the "error" category with any from the above list.

Samuel Lindblom
  • 812
  • 1
  • 6
  • 22
tralston
  • 2,825
  • 3
  • 19
  • 22
  • It appears that the argument to create is a category, similar to a log4j category. It could be, for example, the name of the class doing the logging. The category can be a String (as above) or a Class. Alternatively, one can supply an `org.gradle.api.logging.LogLevel` of `DEGUG`, `INFO`, `WARN`, `ERROR`, `LIFECYCLE` or `QUIET`. Finally, one can supply both a category (as a `String` or `Class`) and a `LogLevel`. – Ian Robertson Dec 17 '13 at 17:26
  • I used the above, and set `System.setProperty ('org.gradle.logging.StyledTextOutput.Style.Success', 'GREEN')` and I used it as `out.withStyle (Style.Success).println (configurations.runtime)` but it does not come out GREEN (just default white, for my Ubuntu gnome-terminal). Any insights into this behavior? – Sonny Jan 05 '16 at 16:46
  • 3
    In case anyone else should wonder: `services` is a property of both `GradleInternal` and `ProjectInternal`. Since I wanted to use colored output from a Gradle plugin written in Java, I needed to cast a `Gradle`/`Project` instance to one of those internal interfaces to get hold of the services property. – Chriki Feb 13 '17 at 10:49
21

Just additional informations completing the accepted answer. Here is the default styles with gradle 4.10

StyledTextOutput.Style.values().each {
    out.style(it).println("This line has the style $it")
}

all styles

Moreoever, you can create multicolor line, like a StringBuilder

out.style(Style.ProgressStatus).text('This is ').style(Style.Failure).text('a multicolor ').style(Style.Identifier).println('line')

multicolor

edit : here is a working example :

import org.gradle.internal.logging.text.StyledTextOutput 
import org.gradle.internal.logging.text.StyledTextOutputFactory
import org.gradle.internal.logging.text.StyledTextOutput.Style

def out = services.get(StyledTextOutputFactory).create("an-ouput")

out.style(Style.ProgressStatus).text('This is ').style(Style.Failure).text('a multicolor ').style(Style.Identifier).println('line')
ToYonos
  • 16,469
  • 2
  • 54
  • 70
  • it does not work: I get > No signature of method: org.gradle.internal.io.LinePerThreadBufferingOutputStream.style() is applicable for argument types: (org.gradle.internal.logging.text.StyledTextOutput$Style) values: [Success] Possible solutions: asType(java.lang.Class), close(), close(), close(), close(), any() – hannes ach Oct 25 '18 at 06:19
  • Even though the above works from command-line, IntelliJ 2019.3 give me a `cannot resolve symbol` for the import of `StyledTextOutput.Style`. However, if I remove the `StyledTextOutput.Style` import and just reference `StyledTextOutput.Style.SuccessHeader` in-line, then IntelliJ stops complaining. – Sean Barbeau Apr 14 '20 at 21:54
4

Is there a simple way to tap into the color printing powers of Gradle/Groovy without importing a lot of packages?

In the interest of exploring more options, without importing packages, you could just use straight ANSI escape codes (not a strictly Gradle/Groovy technology), to format your output. The following is a working example:

task myTask {
    def styler = 'black red green yellow blue magenta cyan white'
        .split().toList().withIndex(30)
        .collectEntries { key, val -> [(key) : { "\033[${val}m${it}\033[0m" }] }

    doLast {
        println "Message: ${styler['red']('Hello')} ${styler['blue']('World')}"
    }
}

Complete code on GitHub

Marco R.
  • 2,667
  • 15
  • 33
1

Similar solution, suggested by ToYonos written on Kotlin DSL

val printStyles by tasks.registering {
    doLast {
       val out = project.serviceOf<org.gradle.internal.logging.text.StyledTextOutputFactory>().create("an-output")
        org.gradle.internal.logging.text.StyledTextOutput.Style.values().forEach {
            out.style(it).println("This line has the style $it")
        }
    }
}
1

Simple example using a custom task written in Kotlin:

import org.gradle.api.DefaultTask
import org.gradle.api.tasks.TaskAction
import org.gradle.internal.logging.text.StyledTextOutput
import org.gradle.internal.logging.text.StyledTextOutputFactory
import javax.inject.Inject

abstract class ExampleTask @Inject constructor(outputFactory: StyledTextOutputFactory) :
    DefaultTask() {

    private val out = outputFactory.create("example-task")

    @TaskAction
    fun run() {
        out.style(StyledTextOutput.Style.Success)
            .text("Hello, ")
            .style(StyledTextOutput.Style.Failure)
            .println("World!")
    }
}
Thomas Keller
  • 5,933
  • 6
  • 48
  • 80
  • This breaks configuration cache as you're passing a Gradle type. Wonder if there's any simple alternative like this but configuration cache compliant – Barrufet Sep 01 '23 at 11:04
0

I added color to gradle by creating an external colorizer and using a shell alias to make a macro to always use the colorizer. This solution will easily scale to work to colorize other commands.

on Mac using tcsh

alias gr ./gradlew !* |& ~/scripts/colorize.pl

or manually run

./gradlew assembleDebug | & ~/scripts/colorize.pl

The colorizer is just a perl script with two special features:

  1. Reads standard input unbuffered
  2. Uses color perl module to optionally colorize output

-- colorize.pl --

   #!/usr/bin/perl
    use warnings;
    use strict;
    use Term::ANSIColor;
    use Term::ANSIColor qw(:constants);
    use IO::Select;
    
    sub colorize($);
    
    # unbuffered output
    $| = 1;
    
    # unbuffered input
    my $inStream = IO::Select->new(\*STDIN);
    my $line;
    while () {
        if ($inStream->can_read(2)) {
            last unless defined($line = get_unbuf_line());
            colorize($line)
        }
    }
    
    sub colorize($) {
        my $line = shift;
        # Example line to colorize
        #    NetInfo.java:142: warning: [unchecked]
        chomp( $line );
        if ($line =~ /([^:]+):([0-9]+): ([a-z]+): ([^ ]+)(.*)/) {
            my $head = $1;
            my $lineNo = $2;
            my $state = $3;
            my $code = $4;
            my $tail = $5;
    
            my $stateColor = WHITE;
            if ("$state" eq "warning") { $stateColor = MAGENTA; }
            if ("$state" eq "error") { $stateColor = RED; }
            print "$head" . YELLOW . ":$lineNo :" . $stateColor . " $state " . YELLOW . $code . RESET . $tail . "\n";
        } elsif ($line =~ /^(BUILD) ([A-Z]+)(.*)/) {
            my $head = $1;
            my $state = $2;
            my $tail = $3;
            my $stateColor = RED;
            if ("$state" eq "SUCCESSFUL") { $stateColor = GREEN; }
            print BOLD . $stateColor . "$line" . RESET  . "\n";
        } elsif ($line =~ /^(> Task) ([^ ]+)(.*)/) {
            print CYAN . $1 . WHITE . " $2" . YELLOW . " $3" . RESET  . "\n";
        } elsif ($line =~ /(.*)(warnings*)(.*)/i) {
            print   $1 . MAGENTA . "$2" . RESET . "$3" . "\n";
        } elsif ($line =~ /(.*)(errors*|unresolved|^e[:] .*)(.*)/i) {
            print   $1 . RED . "$2" . RESET . "$3" . "\n";
        } elsif ($line =~ /(.*)(-----.*|=====.*)/) {
            print   $1 . GREEN . "$2" . RESET . "\n";
        } else {
            print "$line\n";
        }
    }
    
    sub get_unbuf_line {
        my $line="";
        while (sysread(STDIN, my $nextbyte, 1)) {
                return $line if $nextbyte eq "\n";
                $line .= $nextbyte;
        }
        return(undef);
    }
LanDenLabs
  • 1,566
  • 16
  • 10
  • ```line 2: use: command not found line 3: use: command not found line 4: use: command not found line 5: syntax error near unexpected token `(' line 5: ` use Term::ANSIColor qw(:constants);'``` – Reza Dec 14 '21 at 15:36
  • To use Perl - you often need to install modules used by the script. In this case you probably need to install Term::ANSIColor. See this link for how to install modules. https://stackoverflow.com/questions/65865/whats-the-easiest-way-to-install-a-missing-perl-module – LanDenLabs Dec 17 '21 at 18:41