blob: 9360e2d3f7fcc5f521002c4812bdf69e38aa0bff [file] [log] [blame]
include(CMakeParseArguments)
include(SwiftUtils)
# Populate the variable 'args' in the parent scope with a keyword
# argument list. We read the variables options, ${k}_keyword, and
# ACCT_${k} from the parent scope, for each ${k} in the list of
# keyword names other than COMMAND accepted by
# add_custom_command_target.
#
# ${k}_keyword must expand to ${k} if ${k} was passed to
# add_custom_command_target, and be empty otherwise.
#
# ACCT_${k} must expand to the list of arguments to
# add_custom_command_target marked by ${k}, and be empty otherwise.
#
function(_make_acct_argument_list)
set(args)
foreach(k ${ARGN})
if(${k} IN_LIST options)
list(APPEND args ${${k}_keyword})
else()
list(APPEND args ${${k}_keyword} ${ACCT_${k}})
endif()
endforeach()
set(args ${args} PARENT_SCOPE)
endfunction()
# Add a custom command/target pair. Use this instead of
# add_custom_command because it provides proper dependency tracking
# when used with parallel builds and the 'Unix Makefiles' generator.
# See https://www.cmake.org/Bug/view.php?id=10082
#
# The CMake documentation for add_custom_command quoth,
#
# "Do not list the output in more than one independent target that
# may build in parallel or the two instances of the rule may
# conflict (instead use add_custom_target to drive the command and
# make the other targets depend on that one)."
#
# This function implements the suggested pattern.
#
# add_custom_command_target(
# dependency_out_var_name
#
# COMMAND command1 [ARGS] [args1...]
# [COMMAND command2 [ARGS] [args2...] ...]
#
# OUTPUT output1 [output2 ...]
# [MAIN_DEPENDENCY depend]
# [DEPENDS [depends...]]
# [IMPLICIT_DEPENDS <lang1> depend1
# [<lang2> depend2] ...]
# [WORKING_DIRECTORY dir]
# [COMMENT comment] [VERBATIM] [APPEND]
# [ALL]
# [SOURCES src1 [src2...]])
#
# dependency_out_var_name is the name of a variable, to be set in the
# parent scope with the name of a target that all targets using the
# OUTPUT should depend on. For example:
#
# add_custom_command_target(
# TheDependency
# COMMAND echo "int main() {}" ">" z.c
# OUTPUT z.c
# VERBATIM
# DEPENDS z.c.gyb)
#
# add_executable(exe1 z.c)
# add_dependencies(exe1 ${TheDependency})
# add_executable(exe2 z.c)
# add_dependencies(exe2 ${TheDependency})
#
# **Note1**: all COMMAND arguments must immediately follow
# dependency_out_var_name or this function will misbehave.
#
# **Note2**: any subdirectories that define targets dependent on
# OUTPUT ${o} should invoke:
#
# set_source_files_properties(${o} PROPERTIES GENERATED true)
#
# All arguments other than ALL, SOURCES, and dependency_out_var_name
# are forwarded to add_custom_command; arguments ALL, SOURCES, and
# WORKING_DIRECTORY are forwarded to add_custom_target. See the
# documentation of those functions for a description of all arguments.
#
# How This Function Works
#
# CMake offers one way to add new build rules: add_custom_command.
# Most people, however, overlook its actual semantics.
# add_custom_command does *not* create a target. The CMake
# documentation declareth,
#
# "A target created in the same directory (CMakeLists.txt file) that
# specifies any output of the custom command as a source file is
# given a rule to generate the file using the command at build
# time."
#
# Therefore, when two targets built in parallel depend on an output of
# the same custom command, they may race to rebuild that output.
# Hilarity ensues. You might not notice this effect depending on the
# generator you use, but it happens with 'Unix Makefiles'.
#
# By injecting a target into the dependency graph between the custom
# command output and any targets that depend on that output, we force
# the output to be built before starting on any of its dependent
# targets.
function(add_custom_command_target dependency_out_var_name)
# Parse the arguments. We don't look for COMMAND arguments because
# they don't follow the pattern supported by cmake_parse_arguments.
# As a result, they end up in ACCT_UNPARSED_ARGUMENTS and are
# forwarded verbatim.
set(options ALL VERBATIM APPEND IDEMPOTENT EXCLUDE_FROM_ALL)
set(single_value_args
MAIN_DEPENDENCY WORKING_DIRECTORY COMMENT CUSTOM_TARGET_NAME)
set(multi_value_args OUTPUT DEPENDS IMPLICIT_DEPENDS SOURCES)
cmake_parse_arguments(
ACCT # prefix
"${options}" "${single_value_args}" "${multi_value_args}" ${ARGN})
set(ACCT_COMMANDS ${ACCT_UNPARSED_ARGUMENTS})
if("${ACCT_CUSTOM_TARGET_NAME}" STREQUAL "")
# CMake doesn't allow '/' characters in filenames, so replace them with '-'
list(GET ACCT_OUTPUT 0 output_filename)
string(REPLACE "${CMAKE_BINARY_DIR}/" "" target_name "${output_filename}")
string(REPLACE "${CMAKE_SOURCE_DIR}/" "" target_name "${target_name}")
string(REPLACE "${CMAKE_CFG_INTDIR}/" "" target_name "${target_name}")
string(REPLACE "/" "-" target_name "${target_name}")
else()
set(target_name "${ACCT_CUSTOM_TARGET_NAME}")
endif()
if((NOT ACCT_IDEMPOTENT) OR (ACCT_IDEMPOTENT AND NOT TARGET "${target_name}"))
# For each keyword argument k that was passed to this function, set
# ${k}_keyword to ${k}. That will allow us to use the incantation
# '${${k}_keyword} ${ACCT_${k}}' to forward the arguments on.
foreach(var ${options} ${single_value_args} ${multi_value_args})
translate_flag(ACCT_${var} ${var} ${var}_keyword)
endforeach()
_make_acct_argument_list(
OUTPUT MAIN_DEPENDENCY DEPENDS
IMPLICIT_DEPENDS WORKING_DIRECTORY COMMENT VERBATIM APPEND)
add_custom_command(${ACCT_COMMANDS} ${args})
_make_acct_argument_list(ALL WORKING_DIRECTORY SOURCES)
add_custom_target(
"${target_name}" ${args}
DEPENDS ${ACCT_OUTPUT}
COMMENT "${ACCT_OUTPUT}")
set_target_properties(
"${target_name}" PROPERTIES
FOLDER "add_custom_command_target artifacts")
if (ACCT_EXCLUDE_FROM_ALL)
set_target_properties(
"${target_name}" PROPERTIES
EXCLUDE_FROM_ALL TRUE)
endif()
endif()
# "Return" the name of the custom target
set("${dependency_out_var_name}" "${target_name}" PARENT_SCOPE)
endfunction()