Files
CMake/Modules/AddExternalProject.cmake
Brad King c45c862ef4 ENH: Factor argument parsing in AddExternalProject
The add_external_project function parses its arguments and puts them in
properties of the target it creates.  This factors out implementation of
the behavior for use by other functions in the module.
2009-03-30 11:35:50 -04:00

775 lines
25 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 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)
else()
if(cmd MATCHES "cmake")
set(${cfg_cmd_id_var} "cmake" PARENT_SCOPE)
else()
if(cmd MATCHES "config")
set(${cfg_cmd_id_var} "configure" PARENT_SCOPE)
else()
set(${cfg_cmd_id_var} "unknown:${cmd}" PARENT_SCOPE)
endif()
endif()
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)
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_target_property(cmd ${name} AEP_DOWNLOAD_COMMAND)
if(cmd STREQUAL "")
# Explicit empty string means no download step for this project
add_custom_command(
OUTPUT ${sentinels_dir}/${name}-download
COMMAND ${CMAKE_COMMAND} -E touch ${sentinels_dir}/${name}-download
WORKING_DIRECTORY ${sentinels_dir}
COMMENT "No download step for '${name}'"
DEPENDS ${sentinels_dir}/CMakeExternals-directories
VERBATIM
)
return()
else()
if(cmd)
add_custom_command(
OUTPUT ${sentinels_dir}/${name}-download
COMMAND ${cmd}
COMMAND ${CMAKE_COMMAND} -E touch ${sentinels_dir}/${name}-download
WORKING_DIRECTORY ${downloads_dir}
COMMENT "Performing download step for '${name}'"
DEPENDS ${sentinels_dir}/CMakeExternals-directories
VERBATIM
)
return()
else()
# No explicit DOWNLOAD_COMMAND property. Look for other properties
# indicating which download method to use in the logic below...
endif()
endif()
get_target_property(cvs_repository ${name} AEP_CVS_REPOSITORY)
if(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(args -d ${cvs_repository} -q co ${cvs_tag} -d ${name} ${cvs_module})
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}")
add_custom_command(
OUTPUT ${sentinels_dir}/${name}-download
COMMAND ${CVS_EXECUTABLE} ${args}
COMMAND ${CMAKE_COMMAND} -E touch ${sentinels_dir}/${name}-download
WORKING_DIRECTORY ${source_dir}
COMMENT "Performing download step (CVS checkout) for '${name}'"
DEPENDS ${sentinels_dir}/${name}-cvsinfo.txt
VERBATIM
)
return()
endif()
get_target_property(svn_repository ${name} AEP_SVN_REPOSITORY)
if(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(args co ${svn_repository} ${svn_tag} ${name})
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}")
add_custom_command(
OUTPUT ${sentinels_dir}/${name}-download
COMMAND ${Subversion_SVN_EXECUTABLE} ${args}
COMMAND ${CMAKE_COMMAND} -E touch ${sentinels_dir}/${name}-download
WORKING_DIRECTORY ${source_dir}
COMMENT "Performing download step (SVN checkout) for '${name}'"
DEPENDS ${sentinels_dir}/${name}-svninfo.txt
VERBATIM
)
return()
endif()
get_target_property(dir ${name} AEP_DIR)
if(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}")
add_custom_command(
OUTPUT ${sentinels_dir}/${name}-download
COMMAND ${CMAKE_COMMAND} -E remove_directory ${source_dir}/${name}
COMMAND ${CMAKE_COMMAND} -E copy_directory ${abs_dir} ${source_dir}/${name}
COMMAND ${CMAKE_COMMAND} -E touch ${sentinels_dir}/${name}-download
WORKING_DIRECTORY ${source_dir}
COMMENT "Performing download step (DIR copy) for '${name}'"
DEPENDS ${sentinels_dir}/${name}-dirinfo.txt
VERBATIM
)
return()
endif()
get_target_property(tar ${name} AEP_TAR)
if(tar)
mkdir("${source_dir}/${name}")
add_custom_command(
OUTPUT ${sentinels_dir}/${name}-download
COMMAND ${CMAKE_COMMAND} -Dfilename=${tar} -Dtmp=${tmp_dir}/${name} -Ddirectory=${source_dir}/${name} -P ${CMAKE_ROOT}/Modules/UntarFile.cmake
COMMAND ${CMAKE_COMMAND} -E touch ${sentinels_dir}/${name}-download
WORKING_DIRECTORY ${source_dir}
COMMENT "Performing download step (TAR untar) for '${name}'"
DEPENDS ${tar}
VERBATIM
)
return()
endif()
get_target_property(tgz ${name} AEP_TGZ)
if(tgz)
mkdir("${source_dir}/${name}")
add_custom_command(
OUTPUT ${sentinels_dir}/${name}-download
COMMAND ${CMAKE_COMMAND} -Dfilename=${tgz} -Dtmp=${tmp_dir}/${name} -Ddirectory=${source_dir}/${name} -P ${CMAKE_ROOT}/Modules/UntarFile.cmake
COMMAND ${CMAKE_COMMAND} -E touch ${sentinels_dir}/${name}-download
WORKING_DIRECTORY ${source_dir}
COMMENT "Performing download step (TGZ untar) for '${name}'"
DEPENDS ${tgz}
VERBATIM
)
return()
endif()
get_target_property(tgz_url ${name} AEP_TGZ_URL)
if(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}")
add_custom_command(
OUTPUT ${sentinels_dir}/${name}-download
COMMAND ${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
COMMAND ${CMAKE_COMMAND} -E touch ${sentinels_dir}/${name}-download
WORKING_DIRECTORY ${source_dir}
COMMENT "Performing download step (TGZ_URL download and untar) for '${name}'"
DEPENDS ${sentinels_dir}/${name}-urlinfo.txt
VERBATIM
)
return()
endif()
get_target_property(tar_url ${name} AEP_TAR_URL)
if(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}")
add_custom_command(
OUTPUT ${sentinels_dir}/${name}-download
COMMAND ${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
COMMAND ${CMAKE_COMMAND} -E touch ${sentinels_dir}/${name}-download
WORKING_DIRECTORY ${source_dir}
COMMENT "Performing download step (TAR_URL download and untar) for '${name}'"
DEPENDS ${sentinels_dir}/${name}-urlinfo.txt
VERBATIM
)
return()
endif()
message(SEND_ERROR "error: no download info for '${name}'")
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_target_property(cmd ${name} AEP_UPDATE_COMMAND)
if(cmd STREQUAL "")
# Explicit empty string means no update step for this project
add_custom_command(
OUTPUT ${sentinels_dir}/${name}-update
COMMAND ${CMAKE_COMMAND} -E touch ${sentinels_dir}/${name}-update
WORKING_DIRECTORY ${sentinels_dir}
COMMENT "No update step for '${name}'"
DEPENDS ${sentinels_dir}/${name}-download
)
return()
else()
if(cmd)
add_custom_command(
OUTPUT ${sentinels_dir}/${name}-update
COMMAND ${cmd}
COMMAND ${CMAKE_COMMAND} -E touch ${sentinels_dir}/${name}-update
WORKING_DIRECTORY ${source_dir}/${name}
COMMENT "Performing update step for '${name}'"
DEPENDS ${sentinels_dir}/${name}-download
VERBATIM
)
return()
else()
# No explicit UPDATE_COMMAND property. Look for other properties
# indicating which update method to use in the logic below...
endif()
endif()
get_target_property(cvs_repository ${name} AEP_CVS_REPOSITORY)
if(cvs_repository)
if(NOT CVS_EXECUTABLE)
message(FATAL_ERROR "error: could not find cvs for update of ${name}")
endif()
get_property(cvs_tag TARGET ${name} PROPERTY AEP_CVS_TAG)
set(args -d ${cvs_repository} -q up -dP ${cvs_tag})
mkdir("${source_dir}/${name}")
add_custom_command(
OUTPUT ${sentinels_dir}/${name}-update
COMMAND ${CVS_EXECUTABLE} ${args}
WORKING_DIRECTORY ${source_dir}/${name}
COMMENT "Performing update step (CVS update) for '${name}'"
DEPENDS ${sentinels_dir}/${name}-download
VERBATIM
)
# Since the update sentinel is not actually written:
set_property(SOURCE ${sentinels_dir}/${name}-update
PROPERTY SYMBOLIC 1)
return()
endif()
get_target_property(svn_repository ${name} AEP_SVN_REPOSITORY)
if(svn_repository)
if(NOT Subversion_SVN_EXECUTABLE)
message(FATAL_ERROR "error: could not find svn for update of ${name}")
endif()
get_property(svn_tag TARGET ${name} PROPERTY AEP_SVN_TAG)
set(args up ${svn_tag})
mkdir("${source_dir}/${name}")
add_custom_command(
OUTPUT ${sentinels_dir}/${name}-update
COMMAND ${Subversion_SVN_EXECUTABLE} ${args}
WORKING_DIRECTORY ${source_dir}/${name}
COMMENT "Performing update step (SVN update) for '${name}'"
DEPENDS ${sentinels_dir}/${name}-download
VERBATIM
)
# Since the update sentinel is not actually written:
set_property(SOURCE ${sentinels_dir}/${name}-update
PROPERTY SYMBOLIC 1)
return()
endif()
add_custom_command(
OUTPUT ${sentinels_dir}/${name}-update
COMMAND ${CMAKE_COMMAND} -E touch ${sentinels_dir}/${name}-update
WORKING_DIRECTORY ${sentinels_dir}
COMMENT "No update step for '${name}'"
DEPENDS ${sentinels_dir}/${name}-download
VERBATIM
)
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)
get_target_property(cmd ${name} AEP_PATCH_COMMAND)
if(cmd)
add_custom_command(
OUTPUT ${sentinels_dir}/${name}-patch
COMMAND ${cmd}
COMMAND ${CMAKE_COMMAND} -E touch ${sentinels_dir}/${name}-patch
WORKING_DIRECTORY ${source_dir}/${name}
COMMENT "Performing patch step for '${name}'"
DEPENDS ${sentinels_dir}/${name}-download
VERBATIM
)
return()
endif()
add_custom_command(
OUTPUT ${sentinels_dir}/${name}-patch
COMMAND ${CMAKE_COMMAND} -E touch ${sentinels_dir}/${name}-patch
WORKING_DIRECTORY ${sentinels_dir}
COMMENT "No patch step for '${name}'"
DEPENDS ${sentinels_dir}/${name}-download
VERBATIM
)
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_custom_command(
OUTPUT ${sentinels_dir}/${name}-working_dir
COMMAND ${CMAKE_COMMAND} -E make_directory ${working_dir}
COMMAND ${CMAKE_COMMAND} -E touch ${sentinels_dir}/${name}-working_dir
DEPENDS ${sentinels_dir}/${name}-update
${sentinels_dir}/${name}-patch
${file_deps}
VERBATIM
)
get_target_property(cmd ${name} AEP_CONFIGURE_COMMAND)
if(cmd STREQUAL "")
# Explicit empty string means no configure step for this project
add_custom_command(
OUTPUT ${sentinels_dir}/${name}-configure
COMMAND ${CMAKE_COMMAND} -E touch ${sentinels_dir}/${name}-configure
WORKING_DIRECTORY ${working_dir}
COMMENT "No configure step for '${name}'"
DEPENDS ${sentinels_dir}/${name}-working_dir
VERBATIM
)
else()
if(NOT cmd)
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_custom_command(
OUTPUT ${sentinels_dir}/${name}-configure
COMMAND ${cmd}
COMMAND ${CMAKE_COMMAND} -E touch ${sentinels_dir}/${name}-configure
WORKING_DIRECTORY ${working_dir}
COMMENT "Performing configure step for '${name}'"
DEPENDS ${sentinels_dir}/${name}-working_dir
VERBATIM
)
endif()
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_target_property(cmd ${name} AEP_BUILD_COMMAND)
if(cmd STREQUAL "")
# Explicit empty string means no build step for this project
add_custom_command(
OUTPUT ${sentinels_dir}/${name}-build
COMMAND ${CMAKE_COMMAND} -E touch ${sentinels_dir}/${name}-build
WORKING_DIRECTORY ${working_dir}
COMMENT "No build step for '${name}'"
DEPENDS ${sentinels_dir}/${name}-configure
VERBATIM
)
else()
_aep_get_build_command(${name} BUILD cmd)
add_custom_command(
OUTPUT ${sentinels_dir}/${name}-build
COMMAND ${cmd}
COMMAND ${CMAKE_COMMAND} -E touch ${sentinels_dir}/${name}-build
WORKING_DIRECTORY ${working_dir}
COMMENT "Performing build step for '${name}'"
DEPENDS ${sentinels_dir}/${name}-configure
VERBATIM
)
endif()
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_target_property(cmd ${name} AEP_INSTALL_COMMAND)
if(cmd STREQUAL "")
# Explicit empty string means no install step for this project
add_custom_command(
OUTPUT ${sentinels_dir}/${name}-install
COMMAND ${CMAKE_COMMAND} -E touch ${sentinels_dir}/${name}-install
WORKING_DIRECTORY ${working_dir}
COMMENT "No install step for '${name}'"
DEPENDS ${sentinels_dir}/${name}-build
VERBATIM
)
else()
_aep_get_build_command(${name} INSTALL cmd)
add_custom_command(
OUTPUT ${sentinels_dir}/${name}-install
COMMAND ${cmd}
COMMAND ${CMAKE_COMMAND} -E touch ${sentinels_dir}/${name}-install
WORKING_DIRECTORY ${working_dir}
COMMENT "Performing install step for '${name}'"
DEPENDS ${sentinels_dir}/${name}-build
VERBATIM
)
endif()
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
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)