AddExternalProject.cmake 25 KB

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