AddExternalProject.cmake 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669
  1. # Requires CVS CMake for 'function' and '-E touch' and '--build'
  2. find_package(CVS)
  3. find_package(Subversion)
  4. function(_aep_parse_arguments f name ns args)
  5. # Transfer the arguments to this function into target properties for the
  6. # new custom target we just added so that we can set up all the build steps
  7. # correctly based on target properties.
  8. #
  9. # We loop through ARGN and consider the namespace starting with an
  10. # upper-case letter followed by at least two more upper-case letters
  11. # or underscores to be keywords.
  12. set(key)
  13. foreach(arg IN LISTS args)
  14. if(arg MATCHES "^[A-Z][A-Z_][A-Z_]+$" AND
  15. NOT ((arg STREQUAL "${key}") AND (key STREQUAL "COMMAND")) AND
  16. NOT arg MATCHES "^(TRUE|FALSE)$")
  17. # Keyword
  18. set(key "${arg}")
  19. if(_aep_keywords_${f} AND NOT key MATCHES "${_aep_keywords_${f}}")
  20. message(AUTHOR_WARNING "unknown ${f} keyword: ${key}")
  21. endif()
  22. elseif(key)
  23. # Value
  24. if(NOT arg STREQUAL "")
  25. set_property(TARGET ${name} APPEND PROPERTY ${ns}${key} "${arg}")
  26. else()
  27. get_property(have_key TARGET ${name} PROPERTY ${ns}${key} SET)
  28. if(have_key)
  29. get_property(value TARGET ${name} PROPERTY ${ns}${key})
  30. set_property(TARGET ${name} PROPERTY ${ns}${key} "${value};${arg}")
  31. else()
  32. set_property(TARGET ${name} PROPERTY ${ns}${key} "${arg}")
  33. endif()
  34. endif()
  35. else()
  36. # Missing Keyword
  37. message(AUTHOR_WARNING "value with no keyword in ${f}")
  38. endif()
  39. endforeach()
  40. endfunction(_aep_parse_arguments)
  41. 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)
  42. set(base "${CMAKE_BINARY_DIR}/CMakeExternals")
  43. set(${base_dir_var} "${base}" PARENT_SCOPE)
  44. set(${build_dir_var} "${base}/Build" PARENT_SCOPE)
  45. set(${downloads_dir_var} "${base}/Downloads" PARENT_SCOPE)
  46. set(${install_dir_var} "${base}/Install" PARENT_SCOPE)
  47. set(${sentinels_dir_var} "${base}/Sentinels" PARENT_SCOPE)
  48. set(${source_dir_var} "${base}/Source" PARENT_SCOPE)
  49. set(${tmp_dir_var} "${base}/tmp" PARENT_SCOPE)
  50. endfunction(get_external_project_directories)
  51. function(get_configure_build_working_dir name working_dir_var)
  52. get_external_project_directories(base_dir build_dir downloads_dir install_dir
  53. sentinels_dir source_dir tmp_dir)
  54. get_target_property(dir ${name} AEP_CONFIGURE_DIR)
  55. if(dir)
  56. if (IS_ABSOLUTE "${dir}")
  57. set(working_dir "${dir}")
  58. else()
  59. set(working_dir "${source_dir}/${name}/${dir}")
  60. endif()
  61. else()
  62. set(working_dir "${build_dir}/${name}")
  63. endif()
  64. set(${working_dir_var} "${working_dir}" PARENT_SCOPE)
  65. endfunction(get_configure_build_working_dir)
  66. function(get_configure_command_id name cfg_cmd_id_var)
  67. get_target_property(cmd ${name} AEP_CONFIGURE_COMMAND)
  68. if(cmd STREQUAL "")
  69. # Explicit empty string means no configure step for this project
  70. set(${cfg_cmd_id_var} "none" PARENT_SCOPE)
  71. else()
  72. if(NOT cmd)
  73. # Default is "use cmake":
  74. set(${cfg_cmd_id_var} "cmake" PARENT_SCOPE)
  75. else()
  76. # Otherwise we have to analyze the value:
  77. if(cmd MATCHES "^[^;]*/configure")
  78. set(${cfg_cmd_id_var} "configure" PARENT_SCOPE)
  79. elseif(cmd MATCHES "^[^;]*/cmake" AND NOT cmd MATCHES ";-[PE];")
  80. set(${cfg_cmd_id_var} "cmake" PARENT_SCOPE)
  81. elseif(cmd MATCHES "config")
  82. set(${cfg_cmd_id_var} "configure" PARENT_SCOPE)
  83. else()
  84. set(${cfg_cmd_id_var} "unknown:${cmd}" PARENT_SCOPE)
  85. endif()
  86. endif()
  87. endif()
  88. endfunction(get_configure_command_id)
  89. function(_aep_get_build_command name step cmd_var)
  90. set(cmd "${${cmd_var}}")
  91. if(NOT cmd)
  92. set(args)
  93. get_configure_command_id(${name} cfg_cmd_id)
  94. if(cfg_cmd_id STREQUAL "cmake")
  95. # CMake project. Select build command based on generator.
  96. get_target_property(cmake_generator ${name} AEP_CMAKE_GENERATOR)
  97. if("${cmake_generator}" MATCHES "Make" AND
  98. "${cmake_generator}" STREQUAL "${CMAKE_GENERATOR}")
  99. # The project uses the same Makefile generator. Use recursive make.
  100. set(cmd "$(MAKE)")
  101. if(step STREQUAL "INSTALL")
  102. set(args install)
  103. endif()
  104. else()
  105. # Drive the project with "cmake --build".
  106. get_target_property(cmake_command ${name} AEP_CMAKE_COMMAND)
  107. if(cmake_command)
  108. set(cmd "${cmake_command}")
  109. else()
  110. set(cmd "${CMAKE_COMMAND}")
  111. endif()
  112. set(args --build ${working_dir} --config ${CMAKE_CFG_INTDIR})
  113. if(step STREQUAL "INSTALL")
  114. list(APPEND args --target install)
  115. endif()
  116. endif()
  117. else() # if(cfg_cmd_id STREQUAL "configure")
  118. # Non-CMake project. Guess "make" and "make install".
  119. set(cmd "make")
  120. if(step STREQUAL "INSTALL")
  121. set(args install)
  122. endif()
  123. endif()
  124. # Use user-specified arguments instead of default arguments, if any.
  125. get_property(have_args TARGET ${name} PROPERTY AEP_${step}_ARGS SET)
  126. if(have_args)
  127. get_target_property(args ${name} AEP_${step}_ARGS)
  128. endif()
  129. list(APPEND cmd ${args})
  130. endif()
  131. set(${cmd_var} "${cmd}" PARENT_SCOPE)
  132. endfunction(_aep_get_build_command)
  133. function(mkdir d)
  134. file(MAKE_DIRECTORY "${d}")
  135. #message(STATUS "mkdir d='${d}'")
  136. if(NOT EXISTS "${d}")
  137. message(FATAL_ERROR "error: dir '${d}' does not exist after file(MAKE_DIRECTORY call...")
  138. endif()
  139. endfunction(mkdir)
  140. # Pre-compute a regex to match known keywords.
  141. set(_aep_keyword_regex "^(")
  142. set(_aep_keyword_sep)
  143. foreach(key IN ITEMS
  144. COMMAND
  145. COMMENT
  146. DEPENDEES
  147. DEPENDERS
  148. DEPENDS
  149. SYMBOLIC
  150. WORKING_DIRECTORY
  151. )
  152. set(_aep_keyword_regex "${_aep_keyword_regex}${_aep_keyword_sep}${key}")
  153. set(_aep_keyword_sep "|")
  154. endforeach(key)
  155. set(_aep_keyword_regex "${_aep_keyword_regex})$")
  156. set(_aep_keyword_sep)
  157. set(_aep_keywords_add_external_project_step "${_aep_keyword_regex}")
  158. function(add_external_project_step name step)
  159. get_external_project_directories(base_dir build_dir downloads_dir install_dir
  160. sentinels_dir source_dir tmp_dir)
  161. add_custom_command(APPEND
  162. OUTPUT ${sentinels_dir}/${name}-complete
  163. DEPENDS ${sentinels_dir}/${name}-${step}
  164. )
  165. _aep_parse_arguments(add_external_project_step
  166. ${name} AEP_${step}_ "${ARGN}")
  167. # Steps depending on this step.
  168. get_property(dependers TARGET ${name} PROPERTY AEP_${step}_DEPENDERS)
  169. foreach(depender IN LISTS dependers)
  170. add_custom_command(APPEND
  171. OUTPUT ${sentinels_dir}/${name}-${depender}
  172. DEPENDS ${sentinels_dir}/${name}-${step}
  173. )
  174. endforeach()
  175. # Dependencies on files.
  176. get_property(depends TARGET ${name} PROPERTY AEP_${step}_DEPENDS)
  177. # Dependencies on steps.
  178. get_property(dependees TARGET ${name} PROPERTY AEP_${step}_DEPENDEES)
  179. foreach(dependee IN LISTS dependees)
  180. list(APPEND depends ${sentinels_dir}/${name}-${dependee})
  181. endforeach()
  182. # The command to run.
  183. get_property(command TARGET ${name} PROPERTY AEP_${step}_COMMAND)
  184. if(command)
  185. set(comment "Performing ${step} step for '${name}'")
  186. else()
  187. set(comment "No ${step} step for '${name}'")
  188. endif()
  189. get_property(work_dir TARGET ${name} PROPERTY AEP_${step}_WORKING_DIRECTORY)
  190. # Replace list separators.
  191. get_property(sep TARGET ${name} PROPERTY AEP_LIST_SEPARATOR)
  192. if(sep AND command)
  193. string(REPLACE "${sep}" "\\;" command "${command}")
  194. endif()
  195. # Custom comment?
  196. get_property(comment_set TARGET ${name} PROPERTY AEP_${step}_COMMENT SET)
  197. if(comment_set)
  198. get_property(comment TARGET ${name} PROPERTY AEP_${step}_COMMENT)
  199. endif()
  200. # Run every time?
  201. get_property(symbolic TARGET ${name} PROPERTY AEP_${step}_SYMBOLIC)
  202. if(symbolic)
  203. set_property(SOURCE ${sentinels_dir}/${name}-${step} PROPERTY SYMBOLIC 1)
  204. set(touch)
  205. else()
  206. set(touch ${CMAKE_COMMAND} -E touch ${sentinels_dir}/${name}-${step})
  207. endif()
  208. add_custom_command(
  209. OUTPUT ${sentinels_dir}/${name}-${step}
  210. COMMENT ${comment}
  211. COMMAND ${command}
  212. COMMAND ${touch}
  213. DEPENDS ${depends}
  214. WORKING_DIRECTORY ${work_dir}
  215. VERBATIM
  216. )
  217. endfunction(add_external_project_step)
  218. function(add_external_project_download_command name)
  219. get_external_project_directories(base_dir build_dir downloads_dir install_dir
  220. sentinels_dir source_dir tmp_dir)
  221. get_property(cmd_set TARGET ${name} PROPERTY AEP_DOWNLOAD_COMMAND SET)
  222. get_property(cmd TARGET ${name} PROPERTY AEP_DOWNLOAD_COMMAND)
  223. get_property(cvs_repository TARGET ${name} PROPERTY AEP_CVS_REPOSITORY)
  224. get_property(svn_repository TARGET ${name} PROPERTY AEP_SVN_REPOSITORY)
  225. get_property(dir TARGET ${name} PROPERTY AEP_DIR)
  226. get_property(tar TARGET ${name} PROPERTY AEP_TAR)
  227. get_property(tgz TARGET ${name} PROPERTY AEP_TGZ)
  228. get_property(tgz_url TARGET ${name} PROPERTY AEP_TGZ_URL)
  229. get_property(tar_url TARGET ${name} PROPERTY AEP_TAR_URL)
  230. set(depends ${sentinels_dir}/CMakeExternals-directories)
  231. set(comment)
  232. set(work_dir)
  233. if(cmd_set)
  234. set(work_dir ${downloads_dir})
  235. elseif(cvs_repository)
  236. if(NOT CVS_EXECUTABLE)
  237. message(FATAL_ERROR "error: could not find cvs for checkout of ${name}")
  238. endif()
  239. get_target_property(cvs_module ${name} AEP_CVS_MODULE)
  240. if(NOT cvs_module)
  241. message(FATAL_ERROR "error: no CVS_MODULE")
  242. endif()
  243. get_property(cvs_tag TARGET ${name} PROPERTY AEP_CVS_TAG)
  244. set(repository ${cvs_repository})
  245. set(module ${cvs_module})
  246. set(tag ${cvs_tag})
  247. configure_file(
  248. "${CMAKE_ROOT}/Modules/RepositoryInfo.txt.in"
  249. "${sentinels_dir}/${name}-cvsinfo.txt"
  250. @ONLY
  251. )
  252. mkdir("${source_dir}/${name}")
  253. set(work_dir ${source_dir})
  254. set(comment "Performing download step (CVS checkout) for '${name}'")
  255. set(cmd ${CVS_EXECUTABLE} -d ${cvs_repository} -q co ${cvs_tag} -d ${name} ${cvs_module})
  256. list(APPEND depends ${sentinels_dir}/${name}-cvsinfo.txt)
  257. elseif(svn_repository)
  258. if(NOT Subversion_SVN_EXECUTABLE)
  259. message(FATAL_ERROR "error: could not find svn for checkout of ${name}")
  260. endif()
  261. get_property(svn_tag TARGET ${name} PROPERTY AEP_SVN_TAG)
  262. set(repository ${svn_repository})
  263. set(module)
  264. set(tag ${svn_tag})
  265. configure_file(
  266. "${CMAKE_ROOT}/Modules/RepositoryInfo.txt.in"
  267. "${sentinels_dir}/${name}-svninfo.txt"
  268. @ONLY
  269. )
  270. mkdir("${source_dir}/${name}")
  271. set(work_dir ${source_dir})
  272. set(comment "Performing download step (SVN checkout) for '${name}'")
  273. set(cmd ${Subversion_SVN_EXECUTABLE} co ${svn_repository} ${svn_tag} ${name})
  274. list(APPEND depends ${sentinels_dir}/${name}-svninfo.txt)
  275. elseif(dir)
  276. get_filename_component(abs_dir "${dir}" ABSOLUTE)
  277. set(repository "add_external_project DIR")
  278. set(module "${abs_dir}")
  279. set(tag "")
  280. configure_file(
  281. "${CMAKE_ROOT}/Modules/RepositoryInfo.txt.in"
  282. "${sentinels_dir}/${name}-dirinfo.txt"
  283. @ONLY
  284. )
  285. mkdir("${source_dir}/${name}")
  286. set(work_dir ${source_dir})
  287. set(comment "Performing download step (DIR copy) for '${name}'")
  288. set(cmd ${CMAKE_COMMAND} -E remove_directory ${source_dir}/${name}
  289. COMMAND ${CMAKE_COMMAND} -E copy_directory ${abs_dir} ${source_dir}/${name})
  290. list(APPEND depends ${sentinels_dir}/${name}-dirinfo.txt)
  291. elseif(tar)
  292. mkdir("${source_dir}/${name}")
  293. set(work_dir ${source_dir})
  294. set(comment "Performing download step (TAR untar) for '${name}'")
  295. set(cmd ${CMAKE_COMMAND} -Dfilename=${tar} -Dtmp=${tmp_dir}/${name} -Ddirectory=${source_dir}/${name} -P ${CMAKE_ROOT}/Modules/UntarFile.cmake)
  296. list(APPEND depends ${tar})
  297. elseif(tgz)
  298. mkdir("${source_dir}/${name}")
  299. set(work_dir ${source_dir})
  300. set(comment "Performing download step (TGZ untar) for '${name}'")
  301. set(cmd ${CMAKE_COMMAND} -Dfilename=${tgz} -Dtmp=${tmp_dir}/${name} -Ddirectory=${source_dir}/${name} -P ${CMAKE_ROOT}/Modules/UntarFile.cmake)
  302. list(APPEND depends ${tgz})
  303. elseif(tgz_url)
  304. set(repository "add_external_project TGZ_URL")
  305. set(module "${tgz_url}")
  306. set(tag "")
  307. configure_file(
  308. "${CMAKE_ROOT}/Modules/RepositoryInfo.txt.in"
  309. "${sentinels_dir}/${name}-urlinfo.txt"
  310. @ONLY
  311. )
  312. mkdir("${source_dir}/${name}")
  313. set(work_dir ${source_dir})
  314. set(comment "Performing download step (TGZ_URL download and untar) for '${name}'")
  315. set(cmd ${CMAKE_COMMAND} -Dremote=${tgz_url} -Dlocal=${downloads_dir}/${name}.tgz -P ${CMAKE_ROOT}/Modules/DownloadFile.cmake
  316. COMMAND ${CMAKE_COMMAND} -Dfilename=${downloads_dir}/${name} -Dtmp=${tmp_dir}/${name} -Ddirectory=${source_dir}/${name} -P ${CMAKE_ROOT}/Modules/UntarFile.cmake)
  317. list(APPEND depends ${sentinels_dir}/${name}-urlinfo.txt)
  318. elseif(tar_url)
  319. set(repository "add_external_project TAR_URL")
  320. set(module "${tar_url}")
  321. set(tag "")
  322. configure_file(
  323. "${CMAKE_ROOT}/Modules/RepositoryInfo.txt.in"
  324. "${sentinels_dir}/${name}-urlinfo.txt"
  325. @ONLY
  326. )
  327. mkdir("${source_dir}/${name}")
  328. set(work_dir ${source_dir})
  329. set(comment "Performing download step (TAR_URL download and untar) for '${name}'")
  330. set(cmd ${CMAKE_COMMAND} -Dremote=${tar_url} -Dlocal=${downloads_dir}/${name}.tar -P ${CMAKE_ROOT}/Modules/DownloadFile.cmake
  331. COMMAND ${CMAKE_COMMAND} -Dfilename=${downloads_dir}/${name} -Dtmp=${tmp_dir}/${name} -Ddirectory=${source_dir}/${name} -P ${CMAKE_ROOT}/Modules/UntarFile.cmake)
  332. list(APPEND depends ${sentinels_dir}/${name}-urlinfo.txt)
  333. else()
  334. message(SEND_ERROR "error: no download info for '${name}'")
  335. endif()
  336. add_external_project_step(${name} download
  337. COMMENT ${comment}
  338. COMMAND ${cmd}
  339. WORKING_DIRECTORY ${source_dir}
  340. DEPENDS ${depends}
  341. )
  342. endfunction(add_external_project_download_command)
  343. function(add_external_project_update_command name)
  344. get_external_project_directories(base_dir build_dir downloads_dir install_dir
  345. sentinels_dir source_dir tmp_dir)
  346. get_property(cmd TARGET ${name} PROPERTY AEP_UPDATE_COMMAND)
  347. get_property(cvs_repository TARGET ${name} PROPERTY AEP_CVS_REPOSITORY)
  348. get_property(svn_repository TARGET ${name} PROPERTY AEP_SVN_REPOSITORY)
  349. set(work_dir)
  350. set(comment)
  351. set(symbolic)
  352. if(cmd)
  353. set(work_dir ${source_dir}/${name})
  354. elseif(cvs_repository)
  355. if(NOT CVS_EXECUTABLE)
  356. message(FATAL_ERROR "error: could not find cvs for update of ${name}")
  357. endif()
  358. mkdir("${source_dir}/${name}")
  359. set(work_dir ${source_dir}/${name})
  360. set(comment "Performing update step (CVS update) for '${name}'")
  361. get_property(cvs_tag TARGET ${name} PROPERTY AEP_CVS_TAG)
  362. set(cmd ${CVS_EXECUTABLE} -d ${cvs_repository} -q up -dP ${cvs_tag})
  363. set(symbolic 1)
  364. elseif(svn_repository)
  365. if(NOT Subversion_SVN_EXECUTABLE)
  366. message(FATAL_ERROR "error: could not find svn for update of ${name}")
  367. endif()
  368. mkdir("${source_dir}/${name}")
  369. set(work_dir ${source_dir}/${name})
  370. set(comment "Performing update step (SVN update) for '${name}'")
  371. get_property(svn_tag TARGET ${name} PROPERTY AEP_SVN_TAG)
  372. set(cmd ${Subversion_SVN_EXECUTABLE} up ${svn_tag})
  373. set(symbolic 1)
  374. endif()
  375. add_external_project_step(${name} update
  376. COMMENT ${comment}
  377. COMMAND ${cmd}
  378. SYMBOLIC ${symbolic}
  379. WORKING_DIRECTORY ${work_dir}
  380. DEPENDEES download
  381. )
  382. endfunction(add_external_project_update_command)
  383. function(add_external_project_patch_command name)
  384. get_external_project_directories(base_dir build_dir downloads_dir install_dir
  385. sentinels_dir source_dir tmp_dir)
  386. set(work_dir)
  387. get_property(cmd TARGET ${name} PROPERTY AEP_PATCH_COMMAND)
  388. if(cmd)
  389. set(work_dir ${source_dir}/${name})
  390. endif()
  391. add_external_project_step(${name} patch
  392. COMMAND ${cmd}
  393. WORKING_DIRECTORY ${work_dir}
  394. DEPENDEES download
  395. )
  396. endfunction(add_external_project_patch_command)
  397. # TODO: Make sure external projects use the proper compiler
  398. function(add_external_project_configure_command name)
  399. get_external_project_directories(base_dir build_dir downloads_dir install_dir
  400. sentinels_dir source_dir tmp_dir)
  401. get_configure_build_working_dir(${name} working_dir)
  402. # Depend on other external projects (file-level).
  403. set(file_deps)
  404. get_property(deps TARGET ${name} PROPERTY AEP_DEPENDS)
  405. foreach(arg IN LISTS deps)
  406. list(APPEND file_deps ${sentinels_dir}/${arg}-done)
  407. endforeach()
  408. #message(STATUS "info: name='${name}' file_deps='${file_deps}'")
  409. # Create the working_dir for configure, build and install steps:
  410. #
  411. mkdir("${working_dir}")
  412. add_external_project_step(${name} working_dir
  413. COMMENT "Making directory \"${working_dir}\""
  414. COMMAND ${CMAKE_COMMAND} -E make_directory ${working_dir}
  415. DEPENDEES update patch
  416. DEPENDS ${file_deps}
  417. )
  418. get_property(cmd_set TARGET ${name} PROPERTY AEP_CONFIGURE_COMMAND SET)
  419. if(cmd_set)
  420. get_property(cmd TARGET ${name} PROPERTY AEP_CONFIGURE_COMMAND)
  421. else()
  422. get_target_property(cmake_command ${name} AEP_CMAKE_COMMAND)
  423. if(cmake_command)
  424. set(cmd "${cmake_command}")
  425. else()
  426. set(cmd "${CMAKE_COMMAND}")
  427. endif()
  428. get_property(cmake_args TARGET ${name} PROPERTY AEP_CMAKE_ARGS)
  429. list(APPEND cmd ${cmake_args})
  430. get_target_property(cmake_generator ${name} AEP_CMAKE_GENERATOR)
  431. if(cmake_generator)
  432. list(APPEND cmd "-G${cmake_generator}" "${source_dir}/${name}")
  433. endif()
  434. endif()
  435. add_external_project_step(${name} configure
  436. COMMAND ${cmd}
  437. WORKING_DIRECTORY ${working_dir}
  438. DEPENDEES working_dir
  439. )
  440. endfunction(add_external_project_configure_command)
  441. function(add_external_project_build_command name)
  442. get_external_project_directories(base_dir build_dir downloads_dir install_dir
  443. sentinels_dir source_dir tmp_dir)
  444. get_configure_build_working_dir(${name} working_dir)
  445. get_property(cmd_set TARGET ${name} PROPERTY AEP_BUILD_COMMAND SET)
  446. if(cmd_set)
  447. get_property(cmd TARGET ${name} PROPERTY AEP_BUILD_COMMAND)
  448. else()
  449. _aep_get_build_command(${name} BUILD cmd)
  450. endif()
  451. add_external_project_step(${name} build
  452. COMMAND ${cmd}
  453. WORKING_DIRECTORY ${working_dir}
  454. DEPENDEES configure
  455. )
  456. endfunction(add_external_project_build_command)
  457. function(add_external_project_install_command name)
  458. get_external_project_directories(base_dir build_dir downloads_dir install_dir
  459. sentinels_dir source_dir tmp_dir)
  460. get_configure_build_working_dir(${name} working_dir)
  461. get_property(cmd_set TARGET ${name} PROPERTY AEP_INSTALL_COMMAND SET)
  462. if(cmd_set)
  463. get_property(cmd TARGET ${name} PROPERTY AEP_INSTALL_COMMAND)
  464. else()
  465. _aep_get_build_command(${name} INSTALL cmd)
  466. endif()
  467. add_external_project_step(${name} install
  468. COMMAND ${cmd}
  469. WORKING_DIRECTORY ${working_dir}
  470. DEPENDEES build
  471. )
  472. endfunction(add_external_project_install_command)
  473. function(add_CMakeExternals_target)
  474. if(NOT TARGET CMakeExternals)
  475. get_external_project_directories(base_dir build_dir downloads_dir install_dir
  476. sentinels_dir source_dir tmp_dir)
  477. # Make the directories at CMake configure time *and* add a custom command
  478. # to make them at build time. They need to exist at makefile generation
  479. # time for Borland make and wmake so that CMake may generate makefiles
  480. # with "cd C:\short\paths\with\no\spaces" commands in them.
  481. #
  482. # Additionally, the add_custom_command is still used in case somebody
  483. # removes one of the necessary directories and tries to rebuild without
  484. # re-running cmake.
  485. #
  486. mkdir("${build_dir}")
  487. mkdir("${downloads_dir}")
  488. mkdir("${install_dir}")
  489. mkdir("${sentinels_dir}")
  490. mkdir("${source_dir}")
  491. mkdir("${tmp_dir}")
  492. add_custom_command(
  493. OUTPUT ${sentinels_dir}/CMakeExternals-directories
  494. COMMAND ${CMAKE_COMMAND} -E make_directory ${build_dir}
  495. COMMAND ${CMAKE_COMMAND} -E make_directory ${downloads_dir}
  496. COMMAND ${CMAKE_COMMAND} -E make_directory ${install_dir}
  497. COMMAND ${CMAKE_COMMAND} -E make_directory ${sentinels_dir}
  498. COMMAND ${CMAKE_COMMAND} -E make_directory ${source_dir}
  499. COMMAND ${CMAKE_COMMAND} -E make_directory ${tmp_dir}
  500. COMMAND ${CMAKE_COMMAND} -E touch ${sentinels_dir}/CMakeExternals-directories
  501. COMMENT "Creating CMakeExternals directories"
  502. VERBATIM
  503. )
  504. add_custom_target(CMakeExternals ALL
  505. DEPENDS ${sentinels_dir}/CMakeExternals-directories
  506. )
  507. endif()
  508. endfunction(add_CMakeExternals_target)
  509. # Pre-compute a regex to match known keywords.
  510. set(_aep_keyword_regex "^(")
  511. set(_aep_keyword_sep)
  512. foreach(key IN ITEMS
  513. BUILD_ARGS
  514. BUILD_COMMAND
  515. CMAKE_ARGS
  516. CMAKE_COMMAND
  517. CMAKE_GENERATOR
  518. CONFIGURE_COMMAND
  519. CONFIGURE_DIR
  520. CVS_MODULE
  521. CVS_REPOSITORY
  522. CVS_TAG
  523. DEPENDS
  524. DIR
  525. DOWNLOAD_COMMAND
  526. INSTALL_ARGS
  527. INSTALL_COMMAND
  528. LIST_SEPARATOR
  529. PATCH_COMMAND
  530. SVN_REPOSITORY
  531. SVN_TAG
  532. TAR
  533. TAR_URL
  534. TGZ
  535. TGZ_URL
  536. UPDATE_COMMAND
  537. )
  538. set(_aep_keyword_regex "${_aep_keyword_regex}${_aep_keyword_sep}${key}")
  539. set(_aep_keyword_sep "|")
  540. endforeach(key)
  541. set(_aep_keyword_regex "${_aep_keyword_regex})$")
  542. set(_aep_keyword_sep)
  543. set(_aep_keywords_add_external_project "${_aep_keyword_regex}")
  544. function(add_external_project name)
  545. get_external_project_directories(base_dir build_dir downloads_dir install_dir
  546. sentinels_dir source_dir tmp_dir)
  547. # Ensure root CMakeExternals target and directories are created.
  548. # All external projects will depend on this root CMakeExternals target.
  549. #
  550. add_CMakeExternals_target()
  551. # Add a custom target for the external project. The 'complete' step
  552. # depends on all other steps and creates a 'done' mark. A dependent
  553. # external project's 'configure' step depends on the 'done' mark so
  554. # that it rebuilds when this project rebuilds. It is important that
  555. # 'done' is not the output of any custom command so that CMake does
  556. # not propagate build rules to other external project targets.
  557. add_custom_target(${name} ALL DEPENDS ${sentinels_dir}/${name}-complete)
  558. add_custom_command(
  559. OUTPUT ${sentinels_dir}/${name}-complete
  560. COMMENT "Completed '${name}'"
  561. COMMAND ${CMAKE_COMMAND} -E touch ${sentinels_dir}/${name}-complete
  562. COMMAND ${CMAKE_COMMAND} -E touch ${sentinels_dir}/${name}-done
  563. DEPENDS ${sentinels_dir}/${name}-install
  564. VERBATIM
  565. )
  566. set_target_properties(${name} PROPERTIES AEP_IS_EXTERNAL_PROJECT 1)
  567. add_dependencies(${name} CMakeExternals)
  568. _aep_parse_arguments(add_external_project ${name} AEP_ "${ARGN}")
  569. # Depend on other external projects (target-level).
  570. get_property(deps TARGET ${name} PROPERTY AEP_DEPENDS)
  571. foreach(arg IN LISTS deps)
  572. add_dependencies(${name} ${arg})
  573. endforeach()
  574. # Set up custom build steps based on the target properties.
  575. # Each step depends on the previous one.
  576. #
  577. # The target depends on the output of the final step.
  578. # (Already set up above in the DEPENDS of the add_custom_target command.)
  579. #
  580. add_external_project_download_command(${name})
  581. add_external_project_update_command(${name})
  582. add_external_project_patch_command(${name})
  583. add_external_project_configure_command(${name})
  584. add_external_project_build_command(${name})
  585. add_external_project_install_command(${name})
  586. endfunction(add_external_project)