OpenCVDownload.cmake 12 KB

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