How to get colorized output with cmake?

Cmake

Cmake Problem Overview


I want to have message function in CMakeLists.txt which output colorized text. Maybe an escape sequence.

For example:

message("\x1b[31m;This text must be in red")

It don't work. I got:

> Syntax error in cmake code at > > /home/taurus/cmakecolor/CMakeLists.txt:1 > > when parsing string > > \x1b[31m;This text must be in red > > Invalid escape sequence \x

Cmake Solutions


Solution 1 - Cmake

To extend @grim's correct answer, you can make things a bit more convenient by setting up variables to handle the various colours:

if(NOT WIN32)
  string(ASCII 27 Esc)
  set(ColourReset "${Esc}[m")
  set(ColourBold  "${Esc}[1m")
  set(Red         "${Esc}[31m")
  set(Green       "${Esc}[32m")
  set(Yellow      "${Esc}[33m")
  set(Blue        "${Esc}[34m")
  set(Magenta     "${Esc}[35m")
  set(Cyan        "${Esc}[36m")
  set(White       "${Esc}[37m")
  set(BoldRed     "${Esc}[1;31m")
  set(BoldGreen   "${Esc}[1;32m")
  set(BoldYellow  "${Esc}[1;33m")
  set(BoldBlue    "${Esc}[1;34m")
  set(BoldMagenta "${Esc}[1;35m")
  set(BoldCyan    "${Esc}[1;36m")
  set(BoldWhite   "${Esc}[1;37m")
endif()

message("This is normal")
message("${Red}This is Red${ColourReset}")
message("${Green}This is Green${ColourReset}")
message("${Yellow}This is Yellow${ColourReset}")
message("${Blue}This is Blue${ColourReset}")
message("${Magenta}This is Magenta${ColourReset}")
message("${Cyan}This is Cyan${ColourReset}")
message("${White}This is White${ColourReset}")
message("${BoldRed}This is BoldRed${ColourReset}")
message("${BoldGreen}This is BoldGreen${ColourReset}")
message("${BoldYellow}This is BoldYellow${ColourReset}")
message("${BoldBlue}This is BoldBlue${ColourReset}")
message("${BoldMagenta}This is BoldMagenta${ColourReset}")
message("${BoldCyan}This is BoldCyan${ColourReset}")
message("${BoldWhite}This is BoldWhite\n\n${ColourReset}")

If you really fancy pushing the boat out, you can replace the built-in message function with your own which colourises the output depending on the message type:

function(message)
  list(GET ARGV 0 MessageType)
  if(MessageType STREQUAL FATAL_ERROR OR MessageType STREQUAL SEND_ERROR)
    list(REMOVE_AT ARGV 0)
    _message(${MessageType} "${BoldRed}${ARGV}${ColourReset}")
  elseif(MessageType STREQUAL WARNING)
    list(REMOVE_AT ARGV 0)
    _message(${MessageType} "${BoldYellow}${ARGV}${ColourReset}")
  elseif(MessageType STREQUAL AUTHOR_WARNING)
    list(REMOVE_AT ARGV 0)
    _message(${MessageType} "${BoldCyan}${ARGV}${ColourReset}")
  elseif(MessageType STREQUAL STATUS)
    list(REMOVE_AT ARGV 0)
    _message(${MessageType} "${Green}${ARGV}${ColourReset}")
  else()
    _message("${ARGV}")
  endif()
endfunction()

message("No colour at all.")
message(STATUS "\"Colour\" is spelled correctly.")
message(AUTHOR_WARNING "\"Color\" is misspelled.")
message(WARNING "Final warning: spell \"color\" correctly.")
message(SEND_ERROR "Game over.  It's \"colour\", not \"color\".")
message(FATAL_ERROR "And there's no \"z\" in \"colourise\" either.")

I can't say that I recommend overriding the built-in message function in this way, but having said that, I've not found any major problems with doing this either.

Solution 2 - Cmake

A simpler solution is probably just to use CMake's built-in capability for emitting coloured output, i.e. these commands

Different colors

cmake -E cmake_echo_color --normal hello
cmake -E cmake_echo_color --black hello
cmake -E cmake_echo_color --red hello
cmake -E cmake_echo_color --green hello
cmake -E cmake_echo_color --yellow hello
cmake -E cmake_echo_color --blue hello
cmake -E cmake_echo_color --magenta hello
cmake -E cmake_echo_color --cyan hello
cmake -E cmake_echo_color --white hello

Bold text

cmake -E cmake_echo_color --red --bold hello

No new line

cmake -E cmake_echo_color --red --no-newline hello

From CMake you can use the execute_process() command to invoke ${CMAKE_COMMAND}. You could write a convenient function for doing this.

UPDATE: Using cmake_echo_color with execute_process()

As pointed out by @sjm324 running

execute_process(COMMAND ${CMAKE_COMMAND} -E cmake_echo_color --red --bold hello)

does not work. Looking at the implementation https://github.com/Kitware/CMake/blob/10371cd6dcfc1bf601fa3e715734dbe66199e2e4/Source/kwsys/Terminal.c#L160

my guess is that standard output is not attached to the terminal and thus when CMake internally calls isatty() this fails.

I have two hacks that workaround this but really if you care about this you should contact the CMake devs for a less fragile solution

HACK 1: Set CLICOLOR_FORCE=1 environment variable.

execute_process(COMMAND 
  ${CMAKE_COMMAND} -E env CLICOLOR_FORCE=1
    ${CMAKE_COMMAND} -E cmake_echo_color --red --bold hello
)

This isn't a great idea. If you log the output of your build to a file it will have escape sequences in it because you're forcing them to be emitted always.

HACK 2: Set OUTPUT_FILE to be the TTY

Don't do this. HACK 1 is likely more portable.

execute_process(COMMAND
  /usr/bin/tty
  OUTPUT_VARIABLE TTY_NAME
OUTPUT_STRIP_TRAILING_WHITESPACE)

execute_process(COMMAND
  ${CMAKE_COMMAND} -E cmake_echo_color --red --bold hello
  OUTPUT_FILE ${TTY_NAME})

Solution 3 - Cmake

While tedious, you can define a variable that contains an escape character and use it in your message strings

string(ASCII 27 ESCAPE)
message("${ESCAPE}[34mblue${ESCAPE}[0m")

Solution 4 - Cmake

Run execute_process and assign output to VARIBALE, then use the message to print.

No hack


macro ( print_color NAME )
	print ( COLOR ${NAME} "		${NAME}" )
endmacro ()

function  ( text )
	cmake_parse_arguments ( PARSE_ARGV 0 "_TEXT" "BOLD" "COLOR" "" )

	set ( _TEXT_OPTIONS -E cmake_echo_color --no-newline )

	if ( _TEXT_COLOR )
		string ( TOLOWER "${_TEXT_COLOR}" _TEXT_COLOR_LOWER )
		if ( NOT ${_TEXT_COLOR_LOWER} MATCHES "^normal|black|red|green|yellow|blue|magenta|cyan|white" )
			print ( "Only these colours are supported:" )
			print_color ( NORMAL )
			print_color ( BLACK )
			print_color ( RED )
			print_color ( GREEN )
			print_color ( YELLOW )
			print_color ( BLUE )
			print_color ( MAGENTA )
			print_color ( CYAN )
			print_color ( WHITE )
			TEXT ( WARING "Color ${_TEXT_COLOR} is not support." )
		else ()
			list ( APPEND _TEXT_OPTIONS --${_TEXT_COLOR_LOWER} )
		endif ()
	endif ()

	if ( _TEXT_BOLD )
		list ( APPEND _TEXT_OPTIONS --bold )
	endif ()

	execute_process ( COMMAND ${CMAKE_COMMAND} -E env CLICOLOR_FORCE=1 ${CMAKE_COMMAND} ${_TEXT_OPTIONS} ${_TEXT_UNPARSED_ARGUMENTS}
					  OUTPUT_VARIABLE _TEXT_RESULT
					  ECHO_ERROR_VARIABLE
					  )

	set ( TEXT_RESULT ${_TEXT_RESULT} PARENT_SCOPE )
endfunction ()
unset ( print_color )

function ( print )
	text ( ${ARGN} )
	message ( ${TEXT_RESULT} )
endfunction ()

print ( COLOR NORMAL TEST_NORMAL )
print ( BOLD COLOR NORMAL TEST_NORMAL_BOLD )
print ( COLOR BLACK TEST_BLACK )
print ( BOLD COLOR BLACK TEST_BLACK_BOLD )
print ( COLOR RED TEST_RED )
print ( BOLD COLOR RED TEST_RED_BOLD )
print ( COLOR GREEN TEST_GREEN )
print ( BOLD COLOR GREEN TEST_GREEN_BOLD )
print ( COLOR YELLOW TEST_YELLOW )
print ( BOLD COLOR YELLOW TEST_YELLOW_BOLD )
print ( COLOR BLUE TEST_BLUE )
print ( BOLD COLOR BLUE TEST_BLUE_BOLD )
print ( COLOR MAGENTA TEST_MAGENTA )
print ( BOLD COLOR MAGENTA TEST_MAGENTA_BOLD )
print ( COLOR CYAN TEST_CYAN )
print ( BOLD COLOR CYAN TEST_CYAN_BOLD )
print ( COLOR WHITE TEST_WHITE )
print ( BOLD COLOR WHITE TEST_WHITE_BOLD )

<script src="https://gist.github.com/Invincibl-e/3a2dd433c338284cfe95f0d5a6153c06.js"></script>

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionIvan RomanovView Question on Stackoverflow
Solution 1 - CmakeFraserView Answer on Stackoverflow
Solution 2 - CmakedelcypherView Answer on Stackoverflow
Solution 3 - CmakegrimView Answer on Stackoverflow
Solution 4 - CmakeInvincibleView Answer on Stackoverflow