How to create a custom target that only runs a command for a subset of configurations?

CMake 3.20 enables using generator expressions in the add_custom_command(OUTPUT) argument.

I am now trying to create a custom command that only runs a command for a subset of the available CMAKE_CONFIGURATION_TYPES. After a lot of trial and error I came up with an example that works for a single configuration:


# Works with warning when compiling in non-Debug
add_custom_command(
	COMMAND $<$<CONFIG:Debug>:${CMAKE_COMMAND}> $<$<CONFIG:Debug>:-E> $<$<CONFIG:Debug>:touch> $<$<CONFIG:Debug>:${CMAKE_BINARY_DIR}/A_Stamp_$<CONFIG>.stamp> 
	OUTPUT $<$<CONFIG:Debug>:${CMAKE_BINARY_DIR}/A_Stamp_$<CONFIG>.stamp>
)

add_custom_target(
	testConfigCommands
	DEPENDS $<$<CONFIG:Debug>:${CMAKE_BINARY_DIR}/A_Stamp_$<CONFIG>.stamp> 
)

I failed to create an example that works for multiple configurations. I tried something like:


add_custom_command(
	COMMAND $<$<IN_LIST:$<CONFIG>,Debug;Release>:${CMAKE_COMMAND}> $<$<IN_LIST:$<CONFIG>,Debug;Release>:-E> $<$<IN_LIST:$<CONFIG>,Debug;Release>:touch> $<$<IN_LIST:$<CONFIG>,Debug;Release>:${CMAKE_BINARY_DIR}/A_Stamp_$<CONFIG>.stamp>
	OUTPUT $<$<IN_LIST:$<CONFIG>,Debug;Release>:${CMAKE_BINARY_DIR}/A_Stamp_$<CONFIG>.stamp>
)

add_custom_target(
	testConfigCommands
	DEPENDS $<$<IN_LIST:$<CONFIG>,Debug;Release>:${CMAKE_BINARY_DIR}/A_Stamp_$<CONFIG>.stamp>
)

but this will fail with the error:

OUTPUT containing a “<” is not allowed.

I tried escaping the semicolon and other stuff but I keep hitting various errors that I have trouble deciphering. So in my desparation I came here and ask if anybody can post a similar example that creates the stamp-file only when selecting the Release or Debug configuration when using the Visual Studio generator. If that would work without a waring when building the target for a configruation that is not Release or Debug it would be perfect.

It would be even better if the target was only available for a subset of configurations.

First, generator expressions must be specified between quotes to avoid misinterpretation.
Second, genex $<CONFIG:> can take multiple configurations as argument.

So, a simplified command:

add_custom_command(COMMAND "$<$<CONFIG:Debug,Release>:${CMAKE_COMMAND} -E touch ${CMAKE_BINARY_DIR}/A_Stamp_$<CONFIG>.stamp>"
OUTPUT "$<$<CONFIG:Debug,Release>:${CMAKE_BINARY_DIR}/A_Stamp_$<CONFIG>.stamp>")
1 Like

Wow that was quick :smiley:. Thank you very much! I think my major error was to use a semicolon instead of a comma in the configuration lists.

First I used:


add_custom_command(
	COMMAND $<$<CONFIG:Debug,Release>:${CMAKE_COMMAND}> $<$<CONFIG:Debug,Release>:-E> $<$<CONFIG:Debug,Release>:touch> $<$<CONFIG:Debug,Release>:${CMAKE_BINARY_DIR}/A_Stamp_$<CONFIG>.stamp> 
	OUTPUT $<$<CONFIG:Debug,Release>:${CMAKE_BINARY_DIR}/A_Stamp_$<CONFIG>.stamp>
)

which worked. However if I use your example I get an syntax error when the command is run. I suspect that it has something to do with the separators in the path of the stamp file. I am not sure though.

You are right.
Unfortunately, add_custom_command() command has a subtle difference, from other commands, in how arguments are interpreted. So you have to specify each element of the COMMAND args with its own genex:

add_custom_command(COMMAND "$<$<CONFIG:Debug,Release>:${CMAKE_COMMAND}>" "$<$<CONFIG:Debug,Release>:-E>" "$<$<CONFIG:Debug,Release>:touch>" "$<$<CONFIG:Debug,Release>:${CMAKE_BINARY_DIR}/A_Stamp_$<CONFIG>.stamp>"
OUTPUT "$<$<CONFIG:Debug,Release>:${CMAKE_BINARY_DIR}/A_Stamp_$<CONFIG>.stamp>")
2 Likes

For illustration purposes, the COMMAND_EXPAND_LISTS can also be relevant, depending on how you are constructing things. Here’s a refactored example to help demonstrate its use:

cmake_minimum_required(VERSION 3.20)
project(semicolons)

set(outFile ${CMAKE_BINARY_DIR}/A_Stamp_$<CONFIG>.stamp)
set(cmd ${CMAKE_COMMAND} -E touch ${outFile})
set(cmdConfigs Debug Release)
set(configEnabled "$<IN_LIST:$<CONFIG>,${cmdConfigs}>")

add_custom_command(
    OUTPUT "$<${configEnabled}:${outFile}>"
    COMMAND "$<${configEnabled}:${cmd}>"
    COMMAND_EXPAND_LISTS
)

add_custom_target(
	testConfigCommands
    DEPENDS "$<${configEnabled}:${outFile}>"
)

Taking account of Marc’s observation that $<CONFIG:...> can accept multiple configurations, that can be simplified a bit down to this:

cmake_minimum_required(VERSION 3.20)
project(semicolons)

set(outFile ${CMAKE_BINARY_DIR}/A_Stamp_$<CONFIG>.stamp)
set(cmd ${CMAKE_COMMAND} -E touch ${outFile})
set(configEnabled "$<CONFIG:Debug,Release>")   # Only part changed from previous example

add_custom_command(
    OUTPUT "$<${configEnabled}:${outFile}>"
    COMMAND "$<${configEnabled}:${cmd}>"
    COMMAND_EXPAND_LISTS
)

add_custom_target(
	testConfigCommands
    DEPENDS "$<${configEnabled}:${outFile}>"
)