DownloadProject.cmake 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. # Distributed under the OSI-approved MIT License. See accompanying
  2. # file LICENSE or https://github.com/Crascit/DownloadProject for details.
  3. #
  4. # MODULE: DownloadProject
  5. #
  6. # PROVIDES:
  7. # download_project( PROJ projectName
  8. # [PREFIX prefixDir]
  9. # [DOWNLOAD_DIR downloadDir]
  10. # [SOURCE_DIR srcDir]
  11. # [BINARY_DIR binDir]
  12. # [QUIET]
  13. # ...
  14. # )
  15. #
  16. # Provides the ability to download and unpack a tarball, zip file, git repository,
  17. # etc. at configure time (i.e. when the cmake command is run). How the downloaded
  18. # and unpacked contents are used is up to the caller, but the motivating case is
  19. # to download source code which can then be included directly in the build with
  20. # add_subdirectory() after the call to download_project(). Source and build
  21. # directories are set up with this in mind.
  22. #
  23. # The PROJ argument is required. The projectName value will be used to construct
  24. # the following variables upon exit (obviously replace projectName with its actual
  25. # value):
  26. #
  27. # projectName_SOURCE_DIR
  28. # projectName_BINARY_DIR
  29. #
  30. # The SOURCE_DIR and BINARY_DIR arguments are optional and would not typically
  31. # need to be provided. They can be specified if you want the downloaded source
  32. # and build directories to be located in a specific place. The contents of
  33. # projectName_SOURCE_DIR and projectName_BINARY_DIR will be populated with the
  34. # locations used whether you provide SOURCE_DIR/BINARY_DIR or not.
  35. #
  36. # The DOWNLOAD_DIR argument does not normally need to be set. It controls the
  37. # location of the temporary CMake build used to perform the download.
  38. #
  39. # The PREFIX argument can be provided to change the base location of the default
  40. # values of DOWNLOAD_DIR, SOURCE_DIR and BINARY_DIR. If all of those three arguments
  41. # are provided, then PREFIX will have no effect. The default value for PREFIX is
  42. # CMAKE_BINARY_DIR.
  43. #
  44. # The QUIET option can be given if you do not want to show the output associated
  45. # with downloading the specified project.
  46. #
  47. # In addition to the above, any other options are passed through unmodified to
  48. # ExternalProject_Add() to perform the actual download, patch and update steps.
  49. # The following ExternalProject_Add() options are explicitly prohibited (they
  50. # are reserved for use by the download_project() command):
  51. #
  52. # CONFIGURE_COMMAND
  53. # BUILD_COMMAND
  54. # INSTALL_COMMAND
  55. # TEST_COMMAND
  56. #
  57. # Only those ExternalProject_Add() arguments which relate to downloading, patching
  58. # and updating of the project sources are intended to be used. Also note that at
  59. # least one set of download-related arguments are required.
  60. #
  61. # If using CMake 3.2 or later, the UPDATE_DISCONNECTED option can be used to
  62. # prevent a check at the remote end for changes every time CMake is run
  63. # after the first successful download. See the documentation of the ExternalProject
  64. # module for more information. It is likely you will want to use this option if it
  65. # is available to you. Note, however, that the ExternalProject implementation contains
  66. # bugs which result in incorrect handling of the UPDATE_DISCONNECTED option when
  67. # using the URL download method or when specifying a SOURCE_DIR with no download
  68. # method. Fixes for these have been created, the last of which is scheduled for
  69. # inclusion in CMake 3.8.0. Details can be found here:
  70. #
  71. # https://gitlab.kitware.com/cmake/cmake/commit/bdca68388bd57f8302d3c1d83d691034b7ffa70c
  72. # https://gitlab.kitware.com/cmake/cmake/issues/16428
  73. #
  74. # If you experience build errors related to the update step, consider avoiding
  75. # the use of UPDATE_DISCONNECTED.
  76. #
  77. # EXAMPLE USAGE:
  78. #
  79. # include(DownloadProject)
  80. # download_project(PROJ googletest
  81. # GIT_REPOSITORY https://github.com/google/googletest.git
  82. # GIT_TAG master
  83. # UPDATE_DISCONNECTED 1
  84. # QUIET
  85. # )
  86. #
  87. # add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR})
  88. #
  89. #========================================================================================
  90. set(_DownloadProjectDir "${CMAKE_CURRENT_LIST_DIR}")
  91. include(CMakeParseArguments)
  92. function(download_project)
  93. set(options QUIET)
  94. set(oneValueArgs
  95. PROJ
  96. PREFIX
  97. DOWNLOAD_DIR
  98. SOURCE_DIR
  99. BINARY_DIR
  100. # Prevent the following from being passed through
  101. CONFIGURE_COMMAND
  102. BUILD_COMMAND
  103. INSTALL_COMMAND
  104. TEST_COMMAND
  105. )
  106. set(multiValueArgs "")
  107. cmake_parse_arguments(DL_ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
  108. # Hide output if requested
  109. if (DL_ARGS_QUIET)
  110. set(OUTPUT_QUIET "OUTPUT_QUIET")
  111. else()
  112. unset(OUTPUT_QUIET)
  113. message(STATUS "Downloading/updating ${DL_ARGS_PROJ}")
  114. endif()
  115. # Set up where we will put our temporary CMakeLists.txt file and also
  116. # the base point below which the default source and binary dirs will be.
  117. # The prefix must always be an absolute path.
  118. if (NOT DL_ARGS_PREFIX)
  119. set(DL_ARGS_PREFIX "${CMAKE_BINARY_DIR}")
  120. else()
  121. get_filename_component(DL_ARGS_PREFIX "${DL_ARGS_PREFIX}" ABSOLUTE
  122. BASE_DIR "${CMAKE_CURRENT_BINARY_DIR}")
  123. endif()
  124. if (NOT DL_ARGS_DOWNLOAD_DIR)
  125. set(DL_ARGS_DOWNLOAD_DIR "${DL_ARGS_PREFIX}/${DL_ARGS_PROJ}-download")
  126. endif()
  127. # Ensure the caller can know where to find the source and build directories
  128. if (NOT DL_ARGS_SOURCE_DIR)
  129. set(DL_ARGS_SOURCE_DIR "${DL_ARGS_PREFIX}/${DL_ARGS_PROJ}-src")
  130. endif()
  131. if (NOT DL_ARGS_BINARY_DIR)
  132. set(DL_ARGS_BINARY_DIR "${DL_ARGS_PREFIX}/${DL_ARGS_PROJ}-build")
  133. endif()
  134. set(${DL_ARGS_PROJ}_SOURCE_DIR "${DL_ARGS_SOURCE_DIR}" PARENT_SCOPE)
  135. set(${DL_ARGS_PROJ}_BINARY_DIR "${DL_ARGS_BINARY_DIR}" PARENT_SCOPE)
  136. # The way that CLion manages multiple configurations, it causes a copy of
  137. # the CMakeCache.txt to be copied across due to it not expecting there to
  138. # be a project within a project. This causes the hard-coded paths in the
  139. # cache to be copied and builds to fail. To mitigate this, we simply
  140. # remove the cache if it exists before we configure the new project. It
  141. # is safe to do so because it will be re-generated. Since this is only
  142. # executed at the configure step, it should not cause additional builds or
  143. # downloads.
  144. file(REMOVE "${DL_ARGS_DOWNLOAD_DIR}/CMakeCache.txt")
  145. # Create and build a separate CMake project to carry out the download.
  146. # If we've already previously done these steps, they will not cause
  147. # anything to be updated, so extra rebuilds of the project won't occur.
  148. # Make sure to pass through CMAKE_MAKE_PROGRAM in case the main project
  149. # has this set to something not findable on the PATH.
  150. configure_file("${_DownloadProjectDir}/DownloadProject.CMakeLists.cmake.in"
  151. "${DL_ARGS_DOWNLOAD_DIR}/CMakeLists.txt")
  152. execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}"
  153. -D "CMAKE_MAKE_PROGRAM:FILE=${CMAKE_MAKE_PROGRAM}"
  154. .
  155. RESULT_VARIABLE result
  156. ${OUTPUT_QUIET}
  157. WORKING_DIRECTORY "${DL_ARGS_DOWNLOAD_DIR}"
  158. )
  159. if(result)
  160. message(FATAL_ERROR "CMake step for ${DL_ARGS_PROJ} failed: ${result}")
  161. endif()
  162. execute_process(COMMAND ${CMAKE_COMMAND} --build .
  163. RESULT_VARIABLE result
  164. ${OUTPUT_QUIET}
  165. WORKING_DIRECTORY "${DL_ARGS_DOWNLOAD_DIR}"
  166. )
  167. if(result)
  168. message(FATAL_ERROR "Build step for ${DL_ARGS_PROJ} failed: ${result}")
  169. endif()
  170. endfunction()