Skip to content

Instantly share code, notes, and snippets.

@egslava
Created February 14, 2019 18:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save egslava/7333c2aa1cbda9cb0a965ee41512056d to your computer and use it in GitHub Desktop.
Save egslava/7333c2aa1cbda9cb0a965ee41512056d to your computer and use it in GitHub Desktop.
CMake: add_custom_command OUTPUT support for generator expressions
function (add_custom_sources)
set(options VERBATIM)
set(oneValueArgs TARGET OUTPUT)
set(multiValueArgs DEPENDS COMMAND)
# Problem
# -------
# CMake doesn't support generator expressions for OUTPUT directories.
# Thus, it's not gonna work:
# add_custom_command(
# OUTPUT "$<TARGET_FILE_DIR:Main>/gen/file1"
# DEPENDS file1
# COMMAND blah
# )
# target_sources(Main "$<TARGET_FILE_DIR:Arena>/gen/file1")
#
# So there will be problems with Debug/Release configurations
# in MS VS and Qt and Qt Creator and need to work it around.
#
# The section below contains the code with exactly the same logic.
#
# Using
# -----
# add_custom_sources(
# TARGET Main
# OUTPUT "$<TARGET_FILE_DIR:Main>/gen/file1"
# DEPENDS file1
# COMMAND blah
# )
# TARGET - is the target where your generated source code/image/3d-model/anything should be added as a dependency.
#
# How it works
# ------------
# 1. Replacing generator expressions to something unique.
# I map such name "$<TARGET_FILE_DIR:Arena>/gen/file1"
# to somewhat like: "genexpr/TARGET_FILE_DIR_Arena/gen/file1.shadow"
# 2. make all the subdirectories to SHADOW and the original file
# 3. add_custom_command on shadow file. I add TOUCH command to
# create/update shadow file as well
# 4. target_sources(SHADOW_FILE)
# 1. Parsing args
cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} )
if ( ${ARG_VERBATIM} )
set(ARG_VERBATIM VERBATIM)
else()
unset(ARG_VERBATIM)
endif()
# 2. Getting SHADOW name
string(REGEX REPLACE "\\$<(.+)>" "\\1" _no_genex_file ${ARG_OUTPUT} )
string(REPLACE ":" "_" _no_genex_file ${_no_genex_file} )
set(_no_genex_file "${_no_genex_file}.shadow")
set(_no_genex_file "${CMAKE_BINARY_DIR}/genex/${_no_genex_file}")
# 3. Making all the dirs
get_filename_component(_no_genex_dir ${_no_genex_file} DIRECTORY) # _from_root = "~/my/arena-shooter/client/res"
get_filename_component(_output_dir ${ARG_OUTPUT} DIRECTORY)
# 4. Running the command and TOUCHing SHADOW-file
add_custom_command(
OUTPUT ${_no_genex_file}
DEPENDS ${ARG_DEPENDS}
COMMAND ${CMAKE_COMMAND} -E make_directory ${_output_dir}
COMMAND ${CMAKE_COMMAND} -E make_directory ${_no_genex_dir}
COMMAND ${CMAKE_COMMAND} -E touch ${_no_genex_file}
COMMAND ${ARG_COMMAND}
${ARG_VERBATIM}
)
# 5. Adding the dependency, so both files (the original and the shadow)
# will be updated when the source file is changed
target_sources(Arena PUBLIC ${_no_genex_file})
endfunction()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment