CMake/Modules/AddExternalProject.cmake
Brad King 68248be52e ENH: Allow lists in AddExternalProject arguments
The add_external_project function separates its arguments with ';'
separators, so previously no command line argument could contain one.
When specifying CMAKE_ARGS, some -D argument values may need to contain
a semicolon to form lists in the external project cache.

This adds add_external_project argument LIST_SEPARATOR to specify a list
separator string.  The separator is replaced by ';' in arguments to any
command created to drive the external project.  For example:

  add_external_project(...
    LIST_SEPARATOR ::
    CMAKE_ARGS -DSOME_LIST:STRING=A::B::C
    ...)

passes "-DSOME_LIST:STRING=A;B;C" to CMake for the external project.
2009-04-09 13:56:08 -04:00

670 lines
23 KiB
CMake

# Requires CVS CMake for 'function' and '-E touch' and '--build'
find_package(CVS)
find_package(Subversion)
function(_aep_parse_arguments f name ns args)
# Transfer the arguments to this function into target properties for the
# new custom target we just added so that we can set up all the build steps
# correctly based on target properties.
#
# We loop through ARGN and consider the namespace starting with an
# upper-case letter followed by at least two more upper-case letters
# or underscores to be keywords.
set(key)
foreach(arg IN LISTS args)
if(arg MATCHES "^[A-Z][A-Z_][A-Z_]+$" AND
NOT ((arg STREQUAL "${key}") AND (key STREQUAL "COMMAND")) AND
NOT arg MATCHES "^(TRUE|FALSE)$")
# Keyword
set(key "${arg}")
if(_aep_keywords_${f} AND NOT key MATCHES "${_aep_keywords_${f}}")
message(AUTHOR_WARNING "unknown ${f} keyword: ${key}")
endif()
elseif(key)
# Value
if(NOT arg STREQUAL "")
set_property(TARGET ${name} APPEND PROPERTY ${ns}${key} "${arg}")
else()
get_property(have_key TARGET ${name} PROPERTY ${ns}${key} SET)
if(have_key)
get_property(value TARGET ${name} PROPERTY ${ns}${key})
set_property(TARGET ${name} PROPERTY ${ns}${key} "${value};${arg}")
else()
set_property(TARGET ${name} PROPERTY ${ns}${key} "${arg}")
endif()
endif()
else()
# Missing Keyword
message(AUTHOR_WARNING "value with no keyword in ${f}")
endif()
endforeach()
endfunction(_aep_parse_arguments)
function(get_external_project_directories base_dir_var build_dir_var downloads_dir_var install_dir_var sentinels_dir_var source_dir_var tmp_dir_var)
set(base "${CMAKE_BINARY_DIR}/CMakeExternals")
set(${base_dir_var} "${base}" PARENT_SCOPE)
set(${build_dir_var} "${base}/Build" PARENT_SCOPE)
set(${downloads_dir_var} "${base}/Downloads" PARENT_SCOPE)
set(${install_dir_var} "${base}/Install" PARENT_SCOPE)
set(${sentinels_dir_var} "${base}/Sentinels" PARENT_SCOPE)
set(${source_dir_var} "${base}/Source" PARENT_SCOPE)
set(${tmp_dir_var} "${base}/tmp" PARENT_SCOPE)
endfunction(get_external_project_directories)
function(get_configure_build_working_dir name working_dir_var)
get_external_project_directories(base_dir build_dir downloads_dir install_dir
sentinels_dir source_dir tmp_dir)
get_target_property(dir ${name} AEP_CONFIGURE_DIR)
if(dir)
if (IS_ABSOLUTE "${dir}")
set(working_dir "${dir}")
else()
set(working_dir "${source_dir}/${name}/${dir}")
endif()
else()
set(working_dir "${build_dir}/${name}")
endif()
set(${working_dir_var} "${working_dir}" PARENT_SCOPE)
endfunction(get_configure_build_working_dir)
function(get_configure_command_id name cfg_cmd_id_var)
get_target_property(cmd ${name} AEP_CONFIGURE_COMMAND)
if(cmd STREQUAL "")
# Explicit empty string means no configure step for this project
set(${cfg_cmd_id_var} "none" PARENT_SCOPE)
else()
if(NOT cmd)
# Default is "use cmake":
set(${cfg_cmd_id_var} "cmake" PARENT_SCOPE)
else()
# Otherwise we have to analyze the value:
if(cmd MATCHES "^[^;]*/configure")
set(${cfg_cmd_id_var} "configure" PARENT_SCOPE)
elseif(cmd MATCHES "^[^;]*/cmake" AND NOT cmd MATCHES ";-[PE];")
set(${cfg_cmd_id_var} "cmake" PARENT_SCOPE)
elseif(cmd MATCHES "config")
set(${cfg_cmd_id_var} "configure" PARENT_SCOPE)
else()
set(${cfg_cmd_id_var} "unknown:${cmd}" PARENT_SCOPE)
endif()
endif()
endif()
endfunction(get_configure_command_id)
function(_aep_get_build_command name step cmd_var)
set(cmd "${${cmd_var}}")
if(NOT cmd)
set(args)
get_configure_command_id(${name} cfg_cmd_id)
if(cfg_cmd_id STREQUAL "cmake")
# CMake project. Select build command based on generator.
get_target_property(cmake_generator ${name} AEP_CMAKE_GENERATOR)
if("${cmake_generator}" MATCHES "Make" AND
"${cmake_generator}" STREQUAL "${CMAKE_GENERATOR}")
# The project uses the same Makefile generator. Use recursive make.
set(cmd "$(MAKE)")
if(step STREQUAL "INSTALL")
set(args install)
endif()
else()
# Drive the project with "cmake --build".
get_target_property(cmake_command ${name} AEP_CMAKE_COMMAND)
if(cmake_command)
set(cmd "${cmake_command}")
else()
set(cmd "${CMAKE_COMMAND}")
endif()
set(args --build ${working_dir} --config ${CMAKE_CFG_INTDIR})
if(step STREQUAL "INSTALL")
list(APPEND args --target install)
endif()
endif()
else() # if(cfg_cmd_id STREQUAL "configure")
# Non-CMake project. Guess "make" and "make install".
set(cmd "make")
if(step STREQUAL "INSTALL")
set(args install)
endif()
endif()
# Use user-specified arguments instead of default arguments, if any.
get_property(have_args TARGET ${name} PROPERTY AEP_${step}_ARGS SET)
if(have_args)
get_target_property(args ${name} AEP_${step}_ARGS)
endif()
list(APPEND cmd ${args})
endif()
set(${cmd_var} "${cmd}" PARENT_SCOPE)
endfunction(_aep_get_build_command)
function(mkdir d)
file(MAKE_DIRECTORY "${d}")
#message(STATUS "mkdir d='${d}'")
if(NOT EXISTS "${d}")
message(FATAL_ERROR "error: dir '${d}' does not exist after file(MAKE_DIRECTORY call...")
endif()
endfunction(mkdir)
# Pre-compute a regex to match known keywords.
set(_aep_keyword_regex "^(")
set(_aep_keyword_sep)
foreach(key IN ITEMS
COMMAND
COMMENT
DEPENDEES
DEPENDERS
DEPENDS
SYMBOLIC
WORKING_DIRECTORY
)
set(_aep_keyword_regex "${_aep_keyword_regex}${_aep_keyword_sep}${key}")
set(_aep_keyword_sep "|")
endforeach(key)
set(_aep_keyword_regex "${_aep_keyword_regex})$")
set(_aep_keyword_sep)
set(_aep_keywords_add_external_project_step "${_aep_keyword_regex}")
function(add_external_project_step name step)
get_external_project_directories(base_dir build_dir downloads_dir install_dir
sentinels_dir source_dir tmp_dir)
add_custom_command(APPEND
OUTPUT ${sentinels_dir}/${name}-complete
DEPENDS ${sentinels_dir}/${name}-${step}
)
_aep_parse_arguments(add_external_project_step
${name} AEP_${step}_ "${ARGN}")
# Steps depending on this step.
get_property(dependers TARGET ${name} PROPERTY AEP_${step}_DEPENDERS)
foreach(depender IN LISTS dependers)
add_custom_command(APPEND
OUTPUT ${sentinels_dir}/${name}-${depender}
DEPENDS ${sentinels_dir}/${name}-${step}
)
endforeach()
# Dependencies on files.
get_property(depends TARGET ${name} PROPERTY AEP_${step}_DEPENDS)
# Dependencies on steps.
get_property(dependees TARGET ${name} PROPERTY AEP_${step}_DEPENDEES)
foreach(dependee IN LISTS dependees)
list(APPEND depends ${sentinels_dir}/${name}-${dependee})
endforeach()
# The command to run.
get_property(command TARGET ${name} PROPERTY AEP_${step}_COMMAND)
if(command)
set(comment "Performing ${step} step for '${name}'")
else()
set(comment "No ${step} step for '${name}'")
endif()
get_property(work_dir TARGET ${name} PROPERTY AEP_${step}_WORKING_DIRECTORY)
# Replace list separators.
get_property(sep TARGET ${name} PROPERTY AEP_LIST_SEPARATOR)
if(sep AND command)
string(REPLACE "${sep}" "\\;" command "${command}")
endif()
# Custom comment?
get_property(comment_set TARGET ${name} PROPERTY AEP_${step}_COMMENT SET)
if(comment_set)
get_property(comment TARGET ${name} PROPERTY AEP_${step}_COMMENT)
endif()
# Run every time?
get_property(symbolic TARGET ${name} PROPERTY AEP_${step}_SYMBOLIC)
if(symbolic)
set_property(SOURCE ${sentinels_dir}/${name}-${step} PROPERTY SYMBOLIC 1)
set(touch)
else()
set(touch ${CMAKE_COMMAND} -E touch ${sentinels_dir}/${name}-${step})
endif()
add_custom_command(
OUTPUT ${sentinels_dir}/${name}-${step}
COMMENT ${comment}
COMMAND ${command}
COMMAND ${touch}
DEPENDS ${depends}
WORKING_DIRECTORY ${work_dir}
VERBATIM
)
endfunction(add_external_project_step)
function(add_external_project_download_command name)
get_external_project_directories(base_dir build_dir downloads_dir install_dir
sentinels_dir source_dir tmp_dir)
get_property(cmd_set TARGET ${name} PROPERTY AEP_DOWNLOAD_COMMAND SET)
get_property(cmd TARGET ${name} PROPERTY AEP_DOWNLOAD_COMMAND)
get_property(cvs_repository TARGET ${name} PROPERTY AEP_CVS_REPOSITORY)
get_property(svn_repository TARGET ${name} PROPERTY AEP_SVN_REPOSITORY)
get_property(dir TARGET ${name} PROPERTY AEP_DIR)
get_property(tar TARGET ${name} PROPERTY AEP_TAR)
get_property(tgz TARGET ${name} PROPERTY AEP_TGZ)
get_property(tgz_url TARGET ${name} PROPERTY AEP_TGZ_URL)
get_property(tar_url TARGET ${name} PROPERTY AEP_TAR_URL)
set(depends ${sentinels_dir}/CMakeExternals-directories)
set(comment)
set(work_dir)
if(cmd_set)
set(work_dir ${downloads_dir})
elseif(cvs_repository)
if(NOT CVS_EXECUTABLE)
message(FATAL_ERROR "error: could not find cvs for checkout of ${name}")
endif()
get_target_property(cvs_module ${name} AEP_CVS_MODULE)
if(NOT cvs_module)
message(FATAL_ERROR "error: no CVS_MODULE")
endif()
get_property(cvs_tag TARGET ${name} PROPERTY AEP_CVS_TAG)
set(repository ${cvs_repository})
set(module ${cvs_module})
set(tag ${cvs_tag})
configure_file(
"${CMAKE_ROOT}/Modules/RepositoryInfo.txt.in"
"${sentinels_dir}/${name}-cvsinfo.txt"
@ONLY
)
mkdir("${source_dir}/${name}")
set(work_dir ${source_dir})
set(comment "Performing download step (CVS checkout) for '${name}'")
set(cmd ${CVS_EXECUTABLE} -d ${cvs_repository} -q co ${cvs_tag} -d ${name} ${cvs_module})
list(APPEND depends ${sentinels_dir}/${name}-cvsinfo.txt)
elseif(svn_repository)
if(NOT Subversion_SVN_EXECUTABLE)
message(FATAL_ERROR "error: could not find svn for checkout of ${name}")
endif()
get_property(svn_tag TARGET ${name} PROPERTY AEP_SVN_TAG)
set(repository ${svn_repository})
set(module)
set(tag ${svn_tag})
configure_file(
"${CMAKE_ROOT}/Modules/RepositoryInfo.txt.in"
"${sentinels_dir}/${name}-svninfo.txt"
@ONLY
)
mkdir("${source_dir}/${name}")
set(work_dir ${source_dir})
set(comment "Performing download step (SVN checkout) for '${name}'")
set(cmd ${Subversion_SVN_EXECUTABLE} co ${svn_repository} ${svn_tag} ${name})
list(APPEND depends ${sentinels_dir}/${name}-svninfo.txt)
elseif(dir)
get_filename_component(abs_dir "${dir}" ABSOLUTE)
set(repository "add_external_project DIR")
set(module "${abs_dir}")
set(tag "")
configure_file(
"${CMAKE_ROOT}/Modules/RepositoryInfo.txt.in"
"${sentinels_dir}/${name}-dirinfo.txt"
@ONLY
)
mkdir("${source_dir}/${name}")
set(work_dir ${source_dir})
set(comment "Performing download step (DIR copy) for '${name}'")
set(cmd ${CMAKE_COMMAND} -E remove_directory ${source_dir}/${name}
COMMAND ${CMAKE_COMMAND} -E copy_directory ${abs_dir} ${source_dir}/${name})
list(APPEND depends ${sentinels_dir}/${name}-dirinfo.txt)
elseif(tar)
mkdir("${source_dir}/${name}")
set(work_dir ${source_dir})
set(comment "Performing download step (TAR untar) for '${name}'")
set(cmd ${CMAKE_COMMAND} -Dfilename=${tar} -Dtmp=${tmp_dir}/${name} -Ddirectory=${source_dir}/${name} -P ${CMAKE_ROOT}/Modules/UntarFile.cmake)
list(APPEND depends ${tar})
elseif(tgz)
mkdir("${source_dir}/${name}")
set(work_dir ${source_dir})
set(comment "Performing download step (TGZ untar) for '${name}'")
set(cmd ${CMAKE_COMMAND} -Dfilename=${tgz} -Dtmp=${tmp_dir}/${name} -Ddirectory=${source_dir}/${name} -P ${CMAKE_ROOT}/Modules/UntarFile.cmake)
list(APPEND depends ${tgz})
elseif(tgz_url)
set(repository "add_external_project TGZ_URL")
set(module "${tgz_url}")
set(tag "")
configure_file(
"${CMAKE_ROOT}/Modules/RepositoryInfo.txt.in"
"${sentinels_dir}/${name}-urlinfo.txt"
@ONLY
)
mkdir("${source_dir}/${name}")
set(work_dir ${source_dir})
set(comment "Performing download step (TGZ_URL download and untar) for '${name}'")
set(cmd ${CMAKE_COMMAND} -Dremote=${tgz_url} -Dlocal=${downloads_dir}/${name}.tgz -P ${CMAKE_ROOT}/Modules/DownloadFile.cmake
COMMAND ${CMAKE_COMMAND} -Dfilename=${downloads_dir}/${name} -Dtmp=${tmp_dir}/${name} -Ddirectory=${source_dir}/${name} -P ${CMAKE_ROOT}/Modules/UntarFile.cmake)
list(APPEND depends ${sentinels_dir}/${name}-urlinfo.txt)
elseif(tar_url)
set(repository "add_external_project TAR_URL")
set(module "${tar_url}")
set(tag "")
configure_file(
"${CMAKE_ROOT}/Modules/RepositoryInfo.txt.in"
"${sentinels_dir}/${name}-urlinfo.txt"
@ONLY
)
mkdir("${source_dir}/${name}")
set(work_dir ${source_dir})
set(comment "Performing download step (TAR_URL download and untar) for '${name}'")
set(cmd ${CMAKE_COMMAND} -Dremote=${tar_url} -Dlocal=${downloads_dir}/${name}.tar -P ${CMAKE_ROOT}/Modules/DownloadFile.cmake
COMMAND ${CMAKE_COMMAND} -Dfilename=${downloads_dir}/${name} -Dtmp=${tmp_dir}/${name} -Ddirectory=${source_dir}/${name} -P ${CMAKE_ROOT}/Modules/UntarFile.cmake)
list(APPEND depends ${sentinels_dir}/${name}-urlinfo.txt)
else()
message(SEND_ERROR "error: no download info for '${name}'")
endif()
add_external_project_step(${name} download
COMMENT ${comment}
COMMAND ${cmd}
WORKING_DIRECTORY ${source_dir}
DEPENDS ${depends}
)
endfunction(add_external_project_download_command)
function(add_external_project_update_command name)
get_external_project_directories(base_dir build_dir downloads_dir install_dir
sentinels_dir source_dir tmp_dir)
get_property(cmd TARGET ${name} PROPERTY AEP_UPDATE_COMMAND)
get_property(cvs_repository TARGET ${name} PROPERTY AEP_CVS_REPOSITORY)
get_property(svn_repository TARGET ${name} PROPERTY AEP_SVN_REPOSITORY)
set(work_dir)
set(comment)
set(symbolic)
if(cmd)
set(work_dir ${source_dir}/${name})
elseif(cvs_repository)
if(NOT CVS_EXECUTABLE)
message(FATAL_ERROR "error: could not find cvs for update of ${name}")
endif()
mkdir("${source_dir}/${name}")
set(work_dir ${source_dir}/${name})
set(comment "Performing update step (CVS update) for '${name}'")
get_property(cvs_tag TARGET ${name} PROPERTY AEP_CVS_TAG)
set(cmd ${CVS_EXECUTABLE} -d ${cvs_repository} -q up -dP ${cvs_tag})
set(symbolic 1)
elseif(svn_repository)
if(NOT Subversion_SVN_EXECUTABLE)
message(FATAL_ERROR "error: could not find svn for update of ${name}")
endif()
mkdir("${source_dir}/${name}")
set(work_dir ${source_dir}/${name})
set(comment "Performing update step (SVN update) for '${name}'")
get_property(svn_tag TARGET ${name} PROPERTY AEP_SVN_TAG)
set(cmd ${Subversion_SVN_EXECUTABLE} up ${svn_tag})
set(symbolic 1)
endif()
add_external_project_step(${name} update
COMMENT ${comment}
COMMAND ${cmd}
SYMBOLIC ${symbolic}
WORKING_DIRECTORY ${work_dir}
DEPENDEES download
)
endfunction(add_external_project_update_command)
function(add_external_project_patch_command name)
get_external_project_directories(base_dir build_dir downloads_dir install_dir
sentinels_dir source_dir tmp_dir)
set(work_dir)
get_property(cmd TARGET ${name} PROPERTY AEP_PATCH_COMMAND)
if(cmd)
set(work_dir ${source_dir}/${name})
endif()
add_external_project_step(${name} patch
COMMAND ${cmd}
WORKING_DIRECTORY ${work_dir}
DEPENDEES download
)
endfunction(add_external_project_patch_command)
# TODO: Make sure external projects use the proper compiler
function(add_external_project_configure_command name)
get_external_project_directories(base_dir build_dir downloads_dir install_dir
sentinels_dir source_dir tmp_dir)
get_configure_build_working_dir(${name} working_dir)
# Depend on other external projects (file-level).
set(file_deps)
get_property(deps TARGET ${name} PROPERTY AEP_DEPENDS)
foreach(arg IN LISTS deps)
list(APPEND file_deps ${sentinels_dir}/${arg}-done)
endforeach()
#message(STATUS "info: name='${name}' file_deps='${file_deps}'")
# Create the working_dir for configure, build and install steps:
#
mkdir("${working_dir}")
add_external_project_step(${name} working_dir
COMMENT "Making directory \"${working_dir}\""
COMMAND ${CMAKE_COMMAND} -E make_directory ${working_dir}
DEPENDEES update patch
DEPENDS ${file_deps}
)
get_property(cmd_set TARGET ${name} PROPERTY AEP_CONFIGURE_COMMAND SET)
if(cmd_set)
get_property(cmd TARGET ${name} PROPERTY AEP_CONFIGURE_COMMAND)
else()
get_target_property(cmake_command ${name} AEP_CMAKE_COMMAND)
if(cmake_command)
set(cmd "${cmake_command}")
else()
set(cmd "${CMAKE_COMMAND}")
endif()
get_property(cmake_args TARGET ${name} PROPERTY AEP_CMAKE_ARGS)
list(APPEND cmd ${cmake_args})
get_target_property(cmake_generator ${name} AEP_CMAKE_GENERATOR)
if(cmake_generator)
list(APPEND cmd "-G${cmake_generator}" "${source_dir}/${name}")
endif()
endif()
add_external_project_step(${name} configure
COMMAND ${cmd}
WORKING_DIRECTORY ${working_dir}
DEPENDEES working_dir
)
endfunction(add_external_project_configure_command)
function(add_external_project_build_command name)
get_external_project_directories(base_dir build_dir downloads_dir install_dir
sentinels_dir source_dir tmp_dir)
get_configure_build_working_dir(${name} working_dir)
get_property(cmd_set TARGET ${name} PROPERTY AEP_BUILD_COMMAND SET)
if(cmd_set)
get_property(cmd TARGET ${name} PROPERTY AEP_BUILD_COMMAND)
else()
_aep_get_build_command(${name} BUILD cmd)
endif()
add_external_project_step(${name} build
COMMAND ${cmd}
WORKING_DIRECTORY ${working_dir}
DEPENDEES configure
)
endfunction(add_external_project_build_command)
function(add_external_project_install_command name)
get_external_project_directories(base_dir build_dir downloads_dir install_dir
sentinels_dir source_dir tmp_dir)
get_configure_build_working_dir(${name} working_dir)
get_property(cmd_set TARGET ${name} PROPERTY AEP_INSTALL_COMMAND SET)
if(cmd_set)
get_property(cmd TARGET ${name} PROPERTY AEP_INSTALL_COMMAND)
else()
_aep_get_build_command(${name} INSTALL cmd)
endif()
add_external_project_step(${name} install
COMMAND ${cmd}
WORKING_DIRECTORY ${working_dir}
DEPENDEES build
)
endfunction(add_external_project_install_command)
function(add_CMakeExternals_target)
if(NOT TARGET CMakeExternals)
get_external_project_directories(base_dir build_dir downloads_dir install_dir
sentinels_dir source_dir tmp_dir)
# Make the directories at CMake configure time *and* add a custom command
# to make them at build time. They need to exist at makefile generation
# time for Borland make and wmake so that CMake may generate makefiles
# with "cd C:\short\paths\with\no\spaces" commands in them.
#
# Additionally, the add_custom_command is still used in case somebody
# removes one of the necessary directories and tries to rebuild without
# re-running cmake.
#
mkdir("${build_dir}")
mkdir("${downloads_dir}")
mkdir("${install_dir}")
mkdir("${sentinels_dir}")
mkdir("${source_dir}")
mkdir("${tmp_dir}")
add_custom_command(
OUTPUT ${sentinels_dir}/CMakeExternals-directories
COMMAND ${CMAKE_COMMAND} -E make_directory ${build_dir}
COMMAND ${CMAKE_COMMAND} -E make_directory ${downloads_dir}
COMMAND ${CMAKE_COMMAND} -E make_directory ${install_dir}
COMMAND ${CMAKE_COMMAND} -E make_directory ${sentinels_dir}
COMMAND ${CMAKE_COMMAND} -E make_directory ${source_dir}
COMMAND ${CMAKE_COMMAND} -E make_directory ${tmp_dir}
COMMAND ${CMAKE_COMMAND} -E touch ${sentinels_dir}/CMakeExternals-directories
COMMENT "Creating CMakeExternals directories"
VERBATIM
)
add_custom_target(CMakeExternals ALL
DEPENDS ${sentinels_dir}/CMakeExternals-directories
)
endif()
endfunction(add_CMakeExternals_target)
# Pre-compute a regex to match known keywords.
set(_aep_keyword_regex "^(")
set(_aep_keyword_sep)
foreach(key IN ITEMS
BUILD_ARGS
BUILD_COMMAND
CMAKE_ARGS
CMAKE_COMMAND
CMAKE_GENERATOR
CONFIGURE_COMMAND
CONFIGURE_DIR
CVS_MODULE
CVS_REPOSITORY
CVS_TAG
DEPENDS
DIR
DOWNLOAD_COMMAND
INSTALL_ARGS
INSTALL_COMMAND
LIST_SEPARATOR
PATCH_COMMAND
SVN_REPOSITORY
SVN_TAG
TAR
TAR_URL
TGZ
TGZ_URL
UPDATE_COMMAND
)
set(_aep_keyword_regex "${_aep_keyword_regex}${_aep_keyword_sep}${key}")
set(_aep_keyword_sep "|")
endforeach(key)
set(_aep_keyword_regex "${_aep_keyword_regex})$")
set(_aep_keyword_sep)
set(_aep_keywords_add_external_project "${_aep_keyword_regex}")
function(add_external_project name)
get_external_project_directories(base_dir build_dir downloads_dir install_dir
sentinels_dir source_dir tmp_dir)
# Ensure root CMakeExternals target and directories are created.
# All external projects will depend on this root CMakeExternals target.
#
add_CMakeExternals_target()
# Add a custom target for the external project. The 'complete' step
# depends on all other steps and creates a 'done' mark. A dependent
# external project's 'configure' step depends on the 'done' mark so
# that it rebuilds when this project rebuilds. It is important that
# 'done' is not the output of any custom command so that CMake does
# not propagate build rules to other external project targets.
add_custom_target(${name} ALL DEPENDS ${sentinels_dir}/${name}-complete)
add_custom_command(
OUTPUT ${sentinels_dir}/${name}-complete
COMMENT "Completed '${name}'"
COMMAND ${CMAKE_COMMAND} -E touch ${sentinels_dir}/${name}-complete
COMMAND ${CMAKE_COMMAND} -E touch ${sentinels_dir}/${name}-done
DEPENDS ${sentinels_dir}/${name}-install
VERBATIM
)
set_target_properties(${name} PROPERTIES AEP_IS_EXTERNAL_PROJECT 1)
add_dependencies(${name} CMakeExternals)
_aep_parse_arguments(add_external_project ${name} AEP_ "${ARGN}")
# Depend on other external projects (target-level).
get_property(deps TARGET ${name} PROPERTY AEP_DEPENDS)
foreach(arg IN LISTS deps)
add_dependencies(${name} ${arg})
endforeach()
# Set up custom build steps based on the target properties.
# Each step depends on the previous one.
#
# The target depends on the output of the final step.
# (Already set up above in the DEPENDS of the add_custom_target command.)
#
add_external_project_download_command(${name})
add_external_project_update_command(${name})
add_external_project_patch_command(${name})
add_external_project_configure_command(${name})
add_external_project_build_command(${name})
add_external_project_install_command(${name})
endfunction(add_external_project)