AddExternalProject.cmake 25 KB

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