include(CMakeParseArguments) include(SwiftTranslateFlag) # 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}) list(FIND options ${k} option_index) if(${option_index} EQUAL -1) list(APPEND args ${${k}_keyword} ${ACCT_${k}}) else() list(APPEND args ${${k}_keyword}) 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 depend1 # [ 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) 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 "") # Construct a unique name for the custom target. # Use a hash so that the file name does not push the OS limits for filename # length. list(GET ACCT_OUTPUT 0 output_filename) string(MD5 target_md5 "add_custom_command_target${CMAKE_CURRENT_BINARY_DIR}/${output_filename}") get_filename_component(output_filename_basename "${output_filename}" NAME) set(target_name "add_custom_command_target-${target_md5}-${output_filename_basename}") 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}) # Skip generating the target if we are generating an Xcode project only # for IDE use. The volume of dependencies here causes performance problems # in Xcode that make it impractical to use. if(NOT (SWIFT_XCODE_GENERATE_FOR_IDE_ONLY AND "${ACCT_CUSTOM_TARGET_NAME}" STREQUAL "")) _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") endif() endif() # "Return" the name of the custom target if(SWIFT_XCODE_GENERATE_FOR_IDE_ONLY AND "${ACCT_CUSTOM_TARGET_NAME}" STREQUAL "") set("${dependency_out_var_name}" xcode_generate_for_ide_only_dummy PARENT_SCOPE) else() set("${dependency_out_var_name}" "${target_name}" PARENT_SCOPE) endif() endfunction() # A dummy target for XCODE_GENERATE_FOR_IDE_ONLY targets that stands in for # the targets we don't generate in that mode. if(SWIFT_XCODE_GENERATE_FOR_IDE_ONLY) add_custom_command(OUTPUT xcode_generate_for_ide_only_dummy.txt COMMAND echo "This Xcode project is configured for IDE use only and cannot build Swift." COMMAND false) add_custom_target(xcode_generate_for_ide_only_dummy ALL DEPENDS xcode_generate_for_ide_only_dummy.txt) endif()