Files
swift-mirror/cmake/modules/SwiftAddCustomCommandTarget.cmake
Joe Groff 5d5986a44f cmake: Add a SWIFT_XCODE_GENERATE_FOR_IDE_ONLY flag.
This flag disables the generation of dependency targets that are necessary to accurately rebuild Swift code, but which completely tank the Xcode IDE experience. Since Xcode is primarily useful as a source editor/navigator for the compiler C++ code, and Ninja is a more performant and featureful build system for Swift at this point, provide a mode that generates enough of an Xcode project to edit the Swift compiler source, but which can't build it, as a compromise so we can use Xcode's editor alongside a Ninja build environment.

Swift SVN r24186
2015-01-05 22:29:43 +00:00

185 lines
7.0 KiB
CMake

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 <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)
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()