OpenCVDownload.cmake 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. #
  2. # Download and optionally unpack a file
  3. #
  4. # ocv_download(FILENAME p HASH h URL u1 [u2 ...] DESTINATION_DIR d [ID id] [STATUS s] [UNPACK] [RELATIVE_URL])
  5. # FILENAME - filename
  6. # HASH - MD5 hash
  7. # URL - full download url (first nonempty value will be chosen)
  8. # DESTINATION_DIR - file will be copied to this directory
  9. # ID - identifier for project/group of downloaded files
  10. # STATUS - passed variable will be updated in parent scope,
  11. # function will not fail the build in case of download problem if this option is provided,
  12. # but will fail in case when other operations (copy, remove, etc.) failed
  13. # UNPACK - downloaded file will be unpacked to DESTINATION_DIR
  14. # RELATIVE_URL - if set, then URL is treated as a base, and FILENAME will be appended to it
  15. # Note: uses OPENCV_DOWNLOAD_PATH folder as cache, default is <opencv>/.cache
  16. set(HELP_OPENCV_DOWNLOAD_PATH "Cache directory for downloaded files")
  17. if(DEFINED ENV{OPENCV_DOWNLOAD_PATH})
  18. set(OPENCV_DOWNLOAD_PATH "$ENV{OPENCV_DOWNLOAD_PATH}" CACHE PATH "${HELP_OPENCV_DOWNLOAD_PATH}")
  19. endif()
  20. set(OPENCV_DOWNLOAD_PATH "${OpenCV_SOURCE_DIR}/.cache" CACHE PATH "${HELP_OPENCV_DOWNLOAD_PATH}")
  21. set(OPENCV_DOWNLOAD_LOG "${OpenCV_BINARY_DIR}/CMakeDownloadLog.txt")
  22. set(OPENCV_DOWNLOAD_WITH_CURL "${OpenCV_BINARY_DIR}/download_with_curl.sh")
  23. set(OPENCV_DOWNLOAD_WITH_WGET "${OpenCV_BINARY_DIR}/download_with_wget.sh")
  24. set(OPENCV_DOWNLOAD_TRIES_LIST 1 CACHE STRING "List of download tries") # a list
  25. set(OPENCV_DOWNLOAD_PARAMS INACTIVITY_TIMEOUT 60 TIMEOUT 600 CACHE STRING "Download parameters to be passed to file(DOWNLOAD ...)")
  26. mark_as_advanced(OPENCV_DOWNLOAD_TRIES_LIST OPENCV_DOWNLOAD_PARAMS)
  27. # Init download cache directory and log file and helper scripts
  28. if(NOT EXISTS "${OPENCV_DOWNLOAD_PATH}")
  29. file(MAKE_DIRECTORY ${OPENCV_DOWNLOAD_PATH})
  30. endif()
  31. if(NOT EXISTS "${OPENCV_DOWNLOAD_PATH}/.gitignore")
  32. file(WRITE "${OPENCV_DOWNLOAD_PATH}/.gitignore" "*\n")
  33. endif()
  34. file(WRITE "${OPENCV_DOWNLOAD_LOG}" "#use_cache \"${OPENCV_DOWNLOAD_PATH}\"\n")
  35. file(REMOVE "${OPENCV_DOWNLOAD_WITH_CURL}")
  36. file(REMOVE "${OPENCV_DOWNLOAD_WITH_WGET}")
  37. ocv_check_environment_variables(OPENCV_DOWNLOAD_MIRROR_ID)
  38. function(ocv_init_download_mirror)
  39. if(NOT DEFINED OPENCV_DOWNLOAD_MIRROR_ID)
  40. # Run `git remote get-url origin` to get remote source
  41. execute_process(
  42. COMMAND
  43. git remote get-url origin
  44. WORKING_DIRECTORY
  45. ${CMAKE_SOURCE_DIR}
  46. RESULT_VARIABLE
  47. RESULT_STATUS
  48. OUTPUT_VARIABLE
  49. OCV_GIT_ORIGIN_URL_OUTPUT
  50. ERROR_QUIET
  51. )
  52. # if non-git, OCV_GIT_ORIGIN_URL_OUTPUT is empty
  53. if(NOT OCV_GIT_ORIGIN_URL_OUTPUT)
  54. message(STATUS "ocv_init_download: OpenCV source tree is not fetched as git repository. 3rdparty resources will be downloaded from github.com by default.")
  55. return()
  56. else()
  57. # Check if git origin is github.com
  58. string(FIND "${OCV_GIT_ORIGIN_URL_OUTPUT}" "github.com" _found_github)
  59. if(NOT ${_found_github} EQUAL -1)
  60. set(OPENCV_DOWNLOAD_MIRROR_ID "github" CACHE STRING "")
  61. endif()
  62. # Check if git origin is gitcode.net
  63. string(FIND "${OCV_GIT_ORIGIN_URL_OUTPUT}" "gitcode.net" _found_gitcode)
  64. if(NOT ${_found_gitcode} EQUAL -1)
  65. set(OPENCV_DOWNLOAD_MIRROR_ID "gitcode" CACHE STRING "")
  66. endif()
  67. endif()
  68. endif()
  69. if(OPENCV_DOWNLOAD_MIRROR_ID STREQUAL "gitcode" OR OPENCV_DOWNLOAD_MIRROR_ID STREQUAL "custom")
  70. message(STATUS "ocv_init_download: Using ${OPENCV_DOWNLOAD_MIRROR_ID}-hosted mirror to download 3rdparty components.")
  71. ocv_cmake_hook_append(OPENCV_DOWNLOAD_PRE "${CMAKE_CURRENT_SOURCE_DIR}/cmake/mirrors/${OPENCV_DOWNLOAD_MIRROR_ID}.cmake")
  72. elseif(OPENCV_DOWNLOAD_MIRROR_ID STREQUAL "github")
  73. return()
  74. else()
  75. message(STATUS "ocv_init_download: Unable to recognize git server of OpenCV source code. Using github.com to download 3rdparty components.")
  76. endif()
  77. endfunction()
  78. function(ocv_download)
  79. cmake_parse_arguments(DL "UNPACK;RELATIVE_URL" "FILENAME;HASH;DESTINATION_DIR;ID;STATUS" "URL" ${ARGN})
  80. function(ocv_download_log)
  81. file(APPEND "${OPENCV_DOWNLOAD_LOG}" "${ARGN}\n")
  82. endfunction()
  83. ocv_assert(DL_FILENAME)
  84. ocv_assert(DL_HASH)
  85. ocv_assert(DL_URL)
  86. ocv_assert(DL_DESTINATION_DIR)
  87. if((NOT " ${DL_UNPARSED_ARGUMENTS}" STREQUAL " ")
  88. OR DL_FILENAME STREQUAL ""
  89. OR DL_HASH STREQUAL ""
  90. OR DL_URL STREQUAL ""
  91. OR DL_DESTINATION_DIR STREQUAL ""
  92. )
  93. set(msg_level FATAL_ERROR)
  94. if(DEFINED DL_STATUS)
  95. set(${DL_STATUS} FALSE PARENT_SCOPE)
  96. set(msg_level WARNING)
  97. endif()
  98. message(${msg_level} "ERROR: ocv_download() unsupported arguments: ${ARGV}")
  99. return()
  100. endif()
  101. if(DEFINED DL_STATUS)
  102. set(${DL_STATUS} TRUE PARENT_SCOPE)
  103. endif()
  104. ocv_cmake_hook(OPENCV_DOWNLOAD_PRE)
  105. # Check CMake cache for already processed tasks
  106. string(FIND "${DL_DESTINATION_DIR}" "${CMAKE_BINARY_DIR}" DL_BINARY_PATH_POS)
  107. if(DL_BINARY_PATH_POS EQUAL 0)
  108. set(__file_id "${DL_DESTINATION_DIR}/${DL_FILENAME}")
  109. file(RELATIVE_PATH __file_id "${CMAKE_BINARY_DIR}" "${__file_id}")
  110. string(REGEX REPLACE "[^a-zA-Z0-9_]" "_" __file_id "${__file_id}")
  111. if(DL_ID)
  112. string(TOUPPER ${DL_ID} __id)
  113. string(REGEX REPLACE "[^a-zA-Z0-9_]" "_" __id "${__id}")
  114. set(OCV_DOWNLOAD_HASH_NAME "OCV_DOWNLOAD_${__id}_HASH_${__file_id}")
  115. else()
  116. set(OCV_DOWNLOAD_HASH_NAME "OCV_DOWNLOAD_HASH_${__file_id}")
  117. endif()
  118. if(" ${${OCV_DOWNLOAD_HASH_NAME}}" STREQUAL " ${DL_HASH}")
  119. ocv_download_log("#match_hash_in_cmake_cache \"${OCV_DOWNLOAD_HASH_NAME}\"")
  120. return()
  121. endif()
  122. unset("${OCV_DOWNLOAD_HASH_NAME}" CACHE)
  123. else()
  124. set(OCV_DOWNLOAD_HASH_NAME "")
  125. #message(WARNING "Download destination is not in CMAKE_BINARY_DIR=${CMAKE_BINARY_DIR}: ${DL_DESTINATION_DIR}")
  126. endif()
  127. # Select first non-empty url
  128. foreach(url ${DL_URL})
  129. if(url)
  130. set(DL_URL "${url}")
  131. break()
  132. endif()
  133. endforeach()
  134. # Append filename to url if needed
  135. if(DL_RELATIVE_URL)
  136. set(DL_URL "${DL_URL}${DL_FILENAME}")
  137. endif()
  138. set(mode "copy")
  139. if(DL_UNPACK)
  140. set(mode "unpack")
  141. endif()
  142. # Log all calls to file
  143. ocv_download_log("#do_${mode} \"${DL_FILENAME}\" \"${DL_HASH}\" \"${DL_URL}\" \"${DL_DESTINATION_DIR}\"")
  144. # ... and to console
  145. set(__msg_prefix "")
  146. if(DL_ID)
  147. set(__msg_prefix "${DL_ID}: ")
  148. endif()
  149. message(STATUS "${__msg_prefix}Downloading ${DL_FILENAME} from ${DL_URL}")
  150. # Copy mode: check if copy destination exists and is correct
  151. if(NOT DL_UNPACK)
  152. set(COPY_DESTINATION "${DL_DESTINATION_DIR}/${DL_FILENAME}")
  153. if(EXISTS "${COPY_DESTINATION}")
  154. ocv_download_log("#check_md5 \"${COPY_DESTINATION}\"")
  155. file(MD5 "${COPY_DESTINATION}" target_md5)
  156. if(target_md5 STREQUAL DL_HASH)
  157. ocv_download_log("#match_md5 \"${COPY_DESTINATION}\" \"${target_md5}\"")
  158. if(OCV_DOWNLOAD_HASH_NAME)
  159. set(${OCV_DOWNLOAD_HASH_NAME} "${DL_HASH}" CACHE INTERNAL "")
  160. endif()
  161. return()
  162. endif()
  163. ocv_download_log("#mismatch_md5 \"${COPY_DESTINATION}\" \"${target_md5}\"")
  164. else()
  165. ocv_download_log("#missing \"${COPY_DESTINATION}\"")
  166. endif()
  167. endif()
  168. # Check cache first
  169. if(DL_ID)
  170. string(TOLOWER "${DL_ID}" __id)
  171. string(REGEX REPLACE "[^a-zA-Z0-9_/ ]" "_" __id "${__id}")
  172. set(CACHE_CANDIDATE "${OPENCV_DOWNLOAD_PATH}/${__id}/${DL_HASH}-${DL_FILENAME}")
  173. else()
  174. set(CACHE_CANDIDATE "${OPENCV_DOWNLOAD_PATH}/${DL_HASH}-${DL_FILENAME}")
  175. endif()
  176. if(EXISTS "${CACHE_CANDIDATE}")
  177. ocv_download_log("#check_md5 \"${CACHE_CANDIDATE}\"")
  178. file(MD5 "${CACHE_CANDIDATE}" target_md5)
  179. if(NOT target_md5 STREQUAL DL_HASH)
  180. ocv_download_log("#mismatch_md5 \"${CACHE_CANDIDATE}\" \"${target_md5}\"")
  181. ocv_download_log("#delete \"${CACHE_CANDIDATE}\"")
  182. file(REMOVE ${CACHE_CANDIDATE})
  183. endif()
  184. endif()
  185. # Download
  186. if(NOT EXISTS "${CACHE_CANDIDATE}")
  187. ocv_download_log("#cmake_download \"${CACHE_CANDIDATE}\" \"${DL_URL}\"")
  188. foreach(try ${OPENCV_DOWNLOAD_TRIES_LIST})
  189. ocv_download_log("#try ${try}")
  190. file(DOWNLOAD "${DL_URL}" "${CACHE_CANDIDATE}"
  191. STATUS status
  192. LOG __log
  193. ${OPENCV_DOWNLOAD_PARAMS})
  194. if(status EQUAL 0)
  195. break()
  196. endif()
  197. message(STATUS "Try ${try} failed")
  198. endforeach()
  199. if(NOT OPENCV_SKIP_FILE_DOWNLOAD_DUMP) # workaround problem with old CMake versions: "Invalid escape sequence"
  200. string(LENGTH "${__log}" __log_length)
  201. if(__log_length LESS 65536)
  202. string(REPLACE "\n" "\n# " __log "${__log}")
  203. ocv_download_log("# ${__log}\n")
  204. endif()
  205. endif()
  206. if(NOT status EQUAL 0)
  207. set(msg_level FATAL_ERROR)
  208. if(DEFINED DL_STATUS)
  209. set(${DL_STATUS} FALSE PARENT_SCOPE)
  210. set(msg_level WARNING)
  211. endif()
  212. if(status MATCHES "Couldn't resolve host name")
  213. message(STATUS "
  214. =======================================================================
  215. Couldn't download files from the Internet.
  216. Please check the Internet access on this host.
  217. =======================================================================
  218. ")
  219. elseif(status MATCHES "Couldn't connect to server")
  220. message(STATUS "
  221. =======================================================================
  222. Couldn't connect to server from the Internet.
  223. Perhaps direct connections are not allowed in the current network.
  224. To use proxy please check/specify these environment variables:
  225. - http_proxy/https_proxy
  226. - and/or HTTP_PROXY/HTTPS_PROXY
  227. =======================================================================
  228. ")
  229. endif()
  230. message(${msg_level} "${__msg_prefix}Download failed: ${status}
  231. For details please refer to the download log file:
  232. ${OPENCV_DOWNLOAD_LOG}
  233. ")
  234. # write helper scripts for failed downloads
  235. file(APPEND "${OPENCV_DOWNLOAD_WITH_CURL}" "curl --create-dirs --output \"${CACHE_CANDIDATE}\" \"${DL_URL}\"\n")
  236. file(APPEND "${OPENCV_DOWNLOAD_WITH_WGET}" "mkdir -p $(dirname ${CACHE_CANDIDATE}) && wget -O \"${CACHE_CANDIDATE}\" \"${DL_URL}\"\n")
  237. return()
  238. endif()
  239. # Don't remove this code, because EXPECTED_MD5 parameter doesn't fail "file(DOWNLOAD)" step on wrong hash
  240. ocv_download_log("#check_md5 \"${CACHE_CANDIDATE}\"")
  241. file(MD5 "${CACHE_CANDIDATE}" target_md5)
  242. if(NOT target_md5 STREQUAL DL_HASH)
  243. ocv_download_log("#mismatch_md5 \"${CACHE_CANDIDATE}\" \"${target_md5}\"")
  244. set(msg_level FATAL_ERROR)
  245. if(DEFINED DL_STATUS)
  246. set(${DL_STATUS} FALSE PARENT_SCOPE)
  247. set(msg_level WARNING)
  248. endif()
  249. message(${msg_level} "${__msg_prefix}Hash mismatch: ${target_md5}")
  250. return()
  251. endif()
  252. endif()
  253. # Unpack or copy
  254. if(DL_UNPACK)
  255. if(EXISTS "${DL_DESTINATION_DIR}")
  256. ocv_download_log("#remove_unpack \"${DL_DESTINATION_DIR}\"")
  257. file(REMOVE_RECURSE "${DL_DESTINATION_DIR}")
  258. endif()
  259. ocv_download_log("#mkdir \"${DL_DESTINATION_DIR}\"")
  260. file(MAKE_DIRECTORY "${DL_DESTINATION_DIR}")
  261. ocv_download_log("#unpack \"${DL_DESTINATION_DIR}\" \"${CACHE_CANDIDATE}\"")
  262. execute_process(COMMAND "${CMAKE_COMMAND}" -E tar xzf "${CACHE_CANDIDATE}"
  263. WORKING_DIRECTORY "${DL_DESTINATION_DIR}"
  264. RESULT_VARIABLE res)
  265. if(NOT res EQUAL 0)
  266. message(FATAL_ERROR "${__msg_prefix}Unpack failed: ${res}")
  267. endif()
  268. else()
  269. ocv_download_log("#copy \"${COPY_DESTINATION}\" \"${CACHE_CANDIDATE}\"")
  270. execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CACHE_CANDIDATE}" "${COPY_DESTINATION}"
  271. RESULT_VARIABLE res)
  272. if(NOT res EQUAL 0)
  273. message(FATAL_ERROR "${__msg_prefix}Copy failed: ${res}")
  274. endif()
  275. endif()
  276. if(OCV_DOWNLOAD_HASH_NAME)
  277. set(${OCV_DOWNLOAD_HASH_NAME} "${DL_HASH}" CACHE INTERNAL "")
  278. endif()
  279. endfunction()
  280. # ----------------------------------------------------------------------------
  281. # Initialize download in case mirror is used
  282. # ----------------------------------------------------------------------------
  283. ocv_init_download_mirror()