AddExternalProject.cmake 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662
  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. # Custom comment?
  191. get_property(comment_set TARGET ${name} PROPERTY AEP_${step}_COMMENT SET)
  192. if(comment_set)
  193. get_property(comment TARGET ${name} PROPERTY AEP_${step}_COMMENT)
  194. endif()
  195. # Run every time?
  196. get_property(symbolic TARGET ${name} PROPERTY AEP_${step}_SYMBOLIC)
  197. if(symbolic)
  198. set_property(SOURCE ${sentinels_dir}/${name}-${step} PROPERTY SYMBOLIC 1)
  199. set(touch)
  200. else()
  201. set(touch ${CMAKE_COMMAND} -E touch ${sentinels_dir}/${name}-${step})
  202. endif()
  203. add_custom_command(
  204. OUTPUT ${sentinels_dir}/${name}-${step}
  205. COMMENT ${comment}
  206. COMMAND ${command}
  207. COMMAND ${touch}
  208. DEPENDS ${depends}
  209. WORKING_DIRECTORY ${work_dir}
  210. VERBATIM
  211. )
  212. endfunction(add_external_project_step)
  213. function(add_external_project_download_command name)
  214. get_external_project_directories(base_dir build_dir downloads_dir install_dir
  215. sentinels_dir source_dir tmp_dir)
  216. get_property(cmd_set TARGET ${name} PROPERTY AEP_DOWNLOAD_COMMAND SET)
  217. get_property(cmd TARGET ${name} PROPERTY AEP_DOWNLOAD_COMMAND)
  218. get_property(cvs_repository TARGET ${name} PROPERTY AEP_CVS_REPOSITORY)
  219. get_property(svn_repository TARGET ${name} PROPERTY AEP_SVN_REPOSITORY)
  220. get_property(dir TARGET ${name} PROPERTY AEP_DIR)
  221. get_property(tar TARGET ${name} PROPERTY AEP_TAR)
  222. get_property(tgz TARGET ${name} PROPERTY AEP_TGZ)
  223. get_property(tgz_url TARGET ${name} PROPERTY AEP_TGZ_URL)
  224. get_property(tar_url TARGET ${name} PROPERTY AEP_TAR_URL)
  225. set(depends ${sentinels_dir}/CMakeExternals-directories)
  226. set(comment)
  227. set(work_dir)
  228. if(cmd_set)
  229. set(work_dir ${downloads_dir})
  230. elseif(cvs_repository)
  231. if(NOT CVS_EXECUTABLE)
  232. message(FATAL_ERROR "error: could not find cvs for checkout of ${name}")
  233. endif()
  234. get_target_property(cvs_module ${name} AEP_CVS_MODULE)
  235. if(NOT cvs_module)
  236. message(FATAL_ERROR "error: no CVS_MODULE")
  237. endif()
  238. get_property(cvs_tag TARGET ${name} PROPERTY AEP_CVS_TAG)
  239. set(repository ${cvs_repository})
  240. set(module ${cvs_module})
  241. set(tag ${cvs_tag})
  242. configure_file(
  243. "${CMAKE_ROOT}/Modules/RepositoryInfo.txt.in"
  244. "${sentinels_dir}/${name}-cvsinfo.txt"
  245. @ONLY
  246. )
  247. mkdir("${source_dir}/${name}")
  248. set(work_dir ${source_dir})
  249. set(comment "Performing download step (CVS checkout) for '${name}'")
  250. set(cmd ${CVS_EXECUTABLE} -d ${cvs_repository} -q co ${cvs_tag} -d ${name} ${cvs_module})
  251. list(APPEND depends ${sentinels_dir}/${name}-cvsinfo.txt)
  252. elseif(svn_repository)
  253. if(NOT Subversion_SVN_EXECUTABLE)
  254. message(FATAL_ERROR "error: could not find svn for checkout of ${name}")
  255. endif()
  256. get_property(svn_tag TARGET ${name} PROPERTY AEP_SVN_TAG)
  257. set(repository ${svn_repository})
  258. set(module)
  259. set(tag ${svn_tag})
  260. configure_file(
  261. "${CMAKE_ROOT}/Modules/RepositoryInfo.txt.in"
  262. "${sentinels_dir}/${name}-svninfo.txt"
  263. @ONLY
  264. )
  265. mkdir("${source_dir}/${name}")
  266. set(work_dir ${source_dir})
  267. set(comment "Performing download step (SVN checkout) for '${name}'")
  268. set(cmd ${Subversion_SVN_EXECUTABLE} co ${svn_repository} ${svn_tag} ${name})
  269. list(APPEND depends ${sentinels_dir}/${name}-svninfo.txt)
  270. elseif(dir)
  271. get_filename_component(abs_dir "${dir}" ABSOLUTE)
  272. set(repository "add_external_project DIR")
  273. set(module "${abs_dir}")
  274. set(tag "")
  275. configure_file(
  276. "${CMAKE_ROOT}/Modules/RepositoryInfo.txt.in"
  277. "${sentinels_dir}/${name}-dirinfo.txt"
  278. @ONLY
  279. )
  280. mkdir("${source_dir}/${name}")
  281. set(work_dir ${source_dir})
  282. set(comment "Performing download step (DIR copy) for '${name}'")
  283. set(cmd ${CMAKE_COMMAND} -E remove_directory ${source_dir}/${name}
  284. COMMAND ${CMAKE_COMMAND} -E copy_directory ${abs_dir} ${source_dir}/${name})
  285. list(APPEND depends ${sentinels_dir}/${name}-dirinfo.txt)
  286. elseif(tar)
  287. mkdir("${source_dir}/${name}")
  288. set(work_dir ${source_dir})
  289. set(comment "Performing download step (TAR untar) for '${name}'")
  290. set(cmd ${CMAKE_COMMAND} -Dfilename=${tar} -Dtmp=${tmp_dir}/${name} -Ddirectory=${source_dir}/${name} -P ${CMAKE_ROOT}/Modules/UntarFile.cmake)
  291. list(APPEND depends ${tar})
  292. elseif(tgz)
  293. mkdir("${source_dir}/${name}")
  294. set(work_dir ${source_dir})
  295. set(comment "Performing download step (TGZ untar) for '${name}'")
  296. set(cmd ${CMAKE_COMMAND} -Dfilename=${tgz} -Dtmp=${tmp_dir}/${name} -Ddirectory=${source_dir}/${name} -P ${CMAKE_ROOT}/Modules/UntarFile.cmake)
  297. list(APPEND depends ${tgz})
  298. elseif(tgz_url)
  299. set(repository "add_external_project TGZ_URL")
  300. set(module "${tgz_url}")
  301. set(tag "")
  302. configure_file(
  303. "${CMAKE_ROOT}/Modules/RepositoryInfo.txt.in"
  304. "${sentinels_dir}/${name}-urlinfo.txt"
  305. @ONLY
  306. )
  307. mkdir("${source_dir}/${name}")
  308. set(work_dir ${source_dir})
  309. set(comment "Performing download step (TGZ_URL download and untar) for '${name}'")
  310. set(cmd ${CMAKE_COMMAND} -Dremote=${tgz_url} -Dlocal=${downloads_dir}/${name}.tgz -P ${CMAKE_ROOT}/Modules/DownloadFile.cmake
  311. COMMAND ${CMAKE_COMMAND} -Dfilename=${downloads_dir}/${name} -Dtmp=${tmp_dir}/${name} -Ddirectory=${source_dir}/${name} -P ${CMAKE_ROOT}/Modules/UntarFile.cmake)
  312. list(APPEND depends ${sentinels_dir}/${name}-urlinfo.txt)
  313. elseif(tar_url)
  314. set(repository "add_external_project TAR_URL")
  315. set(module "${tar_url}")
  316. set(tag "")
  317. configure_file(
  318. "${CMAKE_ROOT}/Modules/RepositoryInfo.txt.in"
  319. "${sentinels_dir}/${name}-urlinfo.txt"
  320. @ONLY
  321. )
  322. mkdir("${source_dir}/${name}")
  323. set(work_dir ${source_dir})
  324. set(comment "Performing download step (TAR_URL download and untar) for '${name}'")
  325. set(cmd ${CMAKE_COMMAND} -Dremote=${tar_url} -Dlocal=${downloads_dir}/${name}.tar -P ${CMAKE_ROOT}/Modules/DownloadFile.cmake
  326. COMMAND ${CMAKE_COMMAND} -Dfilename=${downloads_dir}/${name} -Dtmp=${tmp_dir}/${name} -Ddirectory=${source_dir}/${name} -P ${CMAKE_ROOT}/Modules/UntarFile.cmake)
  327. list(APPEND depends ${sentinels_dir}/${name}-urlinfo.txt)
  328. else()
  329. message(SEND_ERROR "error: no download info for '${name}'")
  330. endif()
  331. add_external_project_step(${name} download
  332. COMMENT ${comment}
  333. COMMAND ${cmd}
  334. WORKING_DIRECTORY ${source_dir}
  335. DEPENDS ${depends}
  336. )
  337. endfunction(add_external_project_download_command)
  338. function(add_external_project_update_command name)
  339. get_external_project_directories(base_dir build_dir downloads_dir install_dir
  340. sentinels_dir source_dir tmp_dir)
  341. get_property(cmd TARGET ${name} PROPERTY AEP_UPDATE_COMMAND)
  342. get_property(cvs_repository TARGET ${name} PROPERTY AEP_CVS_REPOSITORY)
  343. get_property(svn_repository TARGET ${name} PROPERTY AEP_SVN_REPOSITORY)
  344. set(work_dir)
  345. set(comment)
  346. set(symbolic)
  347. if(cmd)
  348. set(work_dir ${source_dir}/${name})
  349. elseif(cvs_repository)
  350. if(NOT CVS_EXECUTABLE)
  351. message(FATAL_ERROR "error: could not find cvs for update of ${name}")
  352. endif()
  353. mkdir("${source_dir}/${name}")
  354. set(work_dir ${source_dir}/${name})
  355. set(comment "Performing update step (CVS update) for '${name}'")
  356. get_property(cvs_tag TARGET ${name} PROPERTY AEP_CVS_TAG)
  357. set(cmd ${CVS_EXECUTABLE} -d ${cvs_repository} -q up -dP ${cvs_tag})
  358. set(symbolic 1)
  359. elseif(svn_repository)
  360. if(NOT Subversion_SVN_EXECUTABLE)
  361. message(FATAL_ERROR "error: could not find svn for update of ${name}")
  362. endif()
  363. mkdir("${source_dir}/${name}")
  364. set(work_dir ${source_dir}/${name})
  365. set(comment "Performing update step (SVN update) for '${name}'")
  366. get_property(svn_tag TARGET ${name} PROPERTY AEP_SVN_TAG)
  367. set(cmd ${Subversion_SVN_EXECUTABLE} up ${svn_tag})
  368. set(symbolic 1)
  369. endif()
  370. add_external_project_step(${name} update
  371. COMMENT ${comment}
  372. COMMAND ${cmd}
  373. SYMBOLIC ${symbolic}
  374. WORKING_DIRECTORY ${work_dir}
  375. DEPENDEES download
  376. )
  377. endfunction(add_external_project_update_command)
  378. function(add_external_project_patch_command name)
  379. get_external_project_directories(base_dir build_dir downloads_dir install_dir
  380. sentinels_dir source_dir tmp_dir)
  381. set(work_dir)
  382. get_property(cmd TARGET ${name} PROPERTY AEP_PATCH_COMMAND)
  383. if(cmd)
  384. set(work_dir ${source_dir}/${name})
  385. endif()
  386. add_external_project_step(${name} patch
  387. COMMAND ${cmd}
  388. WORKING_DIRECTORY ${work_dir}
  389. DEPENDEES download
  390. )
  391. endfunction(add_external_project_patch_command)
  392. # TODO: Make sure external projects use the proper compiler
  393. function(add_external_project_configure_command name)
  394. get_external_project_directories(base_dir build_dir downloads_dir install_dir
  395. sentinels_dir source_dir tmp_dir)
  396. get_configure_build_working_dir(${name} working_dir)
  397. # Depend on other external projects (file-level).
  398. set(file_deps)
  399. get_property(deps TARGET ${name} PROPERTY AEP_DEPENDS)
  400. foreach(arg IN LISTS deps)
  401. list(APPEND file_deps ${sentinels_dir}/${arg}-done)
  402. endforeach()
  403. #message(STATUS "info: name='${name}' file_deps='${file_deps}'")
  404. # Create the working_dir for configure, build and install steps:
  405. #
  406. mkdir("${working_dir}")
  407. add_external_project_step(${name} working_dir
  408. COMMENT "Making directory \"${working_dir}\""
  409. COMMAND ${CMAKE_COMMAND} -E make_directory ${working_dir}
  410. DEPENDEES update patch
  411. DEPENDS ${file_deps}
  412. )
  413. get_property(cmd_set TARGET ${name} PROPERTY AEP_CONFIGURE_COMMAND SET)
  414. if(cmd_set)
  415. get_property(cmd TARGET ${name} PROPERTY AEP_CONFIGURE_COMMAND)
  416. else()
  417. get_target_property(cmake_command ${name} AEP_CMAKE_COMMAND)
  418. if(cmake_command)
  419. set(cmd "${cmake_command}")
  420. else()
  421. set(cmd "${CMAKE_COMMAND}")
  422. endif()
  423. get_property(cmake_args TARGET ${name} PROPERTY AEP_CMAKE_ARGS)
  424. list(APPEND cmd ${cmake_args})
  425. get_target_property(cmake_generator ${name} AEP_CMAKE_GENERATOR)
  426. if(cmake_generator)
  427. list(APPEND cmd "-G${cmake_generator}" "${source_dir}/${name}")
  428. endif()
  429. endif()
  430. add_external_project_step(${name} configure
  431. COMMAND ${cmd}
  432. WORKING_DIRECTORY ${working_dir}
  433. DEPENDEES working_dir
  434. )
  435. endfunction(add_external_project_configure_command)
  436. function(add_external_project_build_command name)
  437. get_external_project_directories(base_dir build_dir downloads_dir install_dir
  438. sentinels_dir source_dir tmp_dir)
  439. get_configure_build_working_dir(${name} working_dir)
  440. get_property(cmd_set TARGET ${name} PROPERTY AEP_BUILD_COMMAND SET)
  441. if(cmd_set)
  442. get_property(cmd TARGET ${name} PROPERTY AEP_BUILD_COMMAND)
  443. else()
  444. _aep_get_build_command(${name} BUILD cmd)
  445. endif()
  446. add_external_project_step(${name} build
  447. COMMAND ${cmd}
  448. WORKING_DIRECTORY ${working_dir}
  449. DEPENDEES configure
  450. )
  451. endfunction(add_external_project_build_command)
  452. function(add_external_project_install_command name)
  453. get_external_project_directories(base_dir build_dir downloads_dir install_dir
  454. sentinels_dir source_dir tmp_dir)
  455. get_configure_build_working_dir(${name} working_dir)
  456. get_property(cmd_set TARGET ${name} PROPERTY AEP_INSTALL_COMMAND SET)
  457. if(cmd_set)
  458. get_property(cmd TARGET ${name} PROPERTY AEP_INSTALL_COMMAND)
  459. else()
  460. _aep_get_build_command(${name} INSTALL cmd)
  461. endif()
  462. add_external_project_step(${name} install
  463. COMMAND ${cmd}
  464. WORKING_DIRECTORY ${working_dir}
  465. DEPENDEES build
  466. )
  467. endfunction(add_external_project_install_command)
  468. function(add_CMakeExternals_target)
  469. if(NOT TARGET CMakeExternals)
  470. get_external_project_directories(base_dir build_dir downloads_dir install_dir
  471. sentinels_dir source_dir tmp_dir)
  472. # Make the directories at CMake configure time *and* add a custom command
  473. # to make them at build time. They need to exist at makefile generation
  474. # time for Borland make and wmake so that CMake may generate makefiles
  475. # with "cd C:\short\paths\with\no\spaces" commands in them.
  476. #
  477. # Additionally, the add_custom_command is still used in case somebody
  478. # removes one of the necessary directories and tries to rebuild without
  479. # re-running cmake.
  480. #
  481. mkdir("${build_dir}")
  482. mkdir("${downloads_dir}")
  483. mkdir("${install_dir}")
  484. mkdir("${sentinels_dir}")
  485. mkdir("${source_dir}")
  486. mkdir("${tmp_dir}")
  487. add_custom_command(
  488. OUTPUT ${sentinels_dir}/CMakeExternals-directories
  489. COMMAND ${CMAKE_COMMAND} -E make_directory ${build_dir}
  490. COMMAND ${CMAKE_COMMAND} -E make_directory ${downloads_dir}
  491. COMMAND ${CMAKE_COMMAND} -E make_directory ${install_dir}
  492. COMMAND ${CMAKE_COMMAND} -E make_directory ${sentinels_dir}
  493. COMMAND ${CMAKE_COMMAND} -E make_directory ${source_dir}
  494. COMMAND ${CMAKE_COMMAND} -E make_directory ${tmp_dir}
  495. COMMAND ${CMAKE_COMMAND} -E touch ${sentinels_dir}/CMakeExternals-directories
  496. COMMENT "Creating CMakeExternals directories"
  497. VERBATIM
  498. )
  499. add_custom_target(CMakeExternals ALL
  500. DEPENDS ${sentinels_dir}/CMakeExternals-directories
  501. )
  502. endif()
  503. endfunction(add_CMakeExternals_target)
  504. # Pre-compute a regex to match known keywords.
  505. set(_aep_keyword_regex "^(")
  506. set(_aep_keyword_sep)
  507. foreach(key IN ITEMS
  508. BUILD_ARGS
  509. BUILD_COMMAND
  510. CMAKE_ARGS
  511. CMAKE_COMMAND
  512. CMAKE_GENERATOR
  513. CONFIGURE_COMMAND
  514. CONFIGURE_DIR
  515. CVS_MODULE
  516. CVS_REPOSITORY
  517. CVS_TAG
  518. DEPENDS
  519. DIR
  520. DOWNLOAD_COMMAND
  521. INSTALL_ARGS
  522. INSTALL_COMMAND
  523. PATCH_COMMAND
  524. SVN_REPOSITORY
  525. SVN_TAG
  526. TAR
  527. TAR_URL
  528. TGZ
  529. TGZ_URL
  530. UPDATE_COMMAND
  531. )
  532. set(_aep_keyword_regex "${_aep_keyword_regex}${_aep_keyword_sep}${key}")
  533. set(_aep_keyword_sep "|")
  534. endforeach(key)
  535. set(_aep_keyword_regex "${_aep_keyword_regex})$")
  536. set(_aep_keyword_sep)
  537. set(_aep_keywords_add_external_project "${_aep_keyword_regex}")
  538. function(add_external_project name)
  539. get_external_project_directories(base_dir build_dir downloads_dir install_dir
  540. sentinels_dir source_dir tmp_dir)
  541. # Ensure root CMakeExternals target and directories are created.
  542. # All external projects will depend on this root CMakeExternals target.
  543. #
  544. add_CMakeExternals_target()
  545. # Add a custom target for the external project. The 'complete' step
  546. # depends on all other steps and creates a 'done' mark. A dependent
  547. # external project's 'configure' step depends on the 'done' mark so
  548. # that it rebuilds when this project rebuilds. It is important that
  549. # 'done' is not the output of any custom command so that CMake does
  550. # not propagate build rules to other external project targets.
  551. add_custom_target(${name} ALL DEPENDS ${sentinels_dir}/${name}-complete)
  552. add_custom_command(
  553. OUTPUT ${sentinels_dir}/${name}-complete
  554. COMMENT "Completed '${name}'"
  555. COMMAND ${CMAKE_COMMAND} -E touch ${sentinels_dir}/${name}-complete
  556. COMMAND ${CMAKE_COMMAND} -E touch ${sentinels_dir}/${name}-done
  557. DEPENDS ${sentinels_dir}/${name}-install
  558. VERBATIM
  559. )
  560. set_target_properties(${name} PROPERTIES AEP_IS_EXTERNAL_PROJECT 1)
  561. add_dependencies(${name} CMakeExternals)
  562. _aep_parse_arguments(add_external_project ${name} AEP_ "${ARGN}")
  563. # Depend on other external projects (target-level).
  564. get_property(deps TARGET ${name} PROPERTY AEP_DEPENDS)
  565. foreach(arg IN LISTS deps)
  566. add_dependencies(${name} ${arg})
  567. endforeach()
  568. # Set up custom build steps based on the target properties.
  569. # Each step depends on the previous one.
  570. #
  571. # The target depends on the output of the final step.
  572. # (Already set up above in the DEPENDS of the add_custom_target command.)
  573. #
  574. add_external_project_download_command(${name})
  575. add_external_project_update_command(${name})
  576. add_external_project_patch_command(${name})
  577. add_external_project_configure_command(${name})
  578. add_external_project_build_command(${name})
  579. add_external_project_install_command(${name})
  580. endfunction(add_external_project)