vcpkg/scripts/cmake/vcpkg_download_distfile.cmake

209 lines
9.7 KiB
CMake

## # vcpkg_download_distfile
##
## Download and cache a file needed for this port.
##
## This helper should always be used instead of CMake's built-in `file(DOWNLOAD)` command.
##
## ## Usage
## ```cmake
## vcpkg_download_distfile(
## <OUT_VARIABLE>
## URLS <http://mainUrl> <http://mirror1>...
## FILENAME <output.zip>
## SHA512 <5981de...>
## )
## ```
## ## Parameters
## ### OUT_VARIABLE
## This variable will be set to the full path to the downloaded file. This can then immediately be passed in to [`vcpkg_extract_source_archive`](vcpkg_extract_source_archive.md) for sources.
##
## ### URLS
## A list of URLs to be consulted. They will be tried in order until one of the downloaded files successfully matches the SHA512 given.
##
## ### FILENAME
## The local name for the file. Files are shared between ports, so the file may need to be renamed to make it clearly attributed to this port and avoid conflicts.
##
## ### SHA512
## The expected hash for the file.
##
## If this doesn't match the downloaded version, the build will be terminated with a message describing the mismatch.
##
## ### QUIET
## Suppress output on cache hit
##
## ### SKIP_SHA512
## Skip SHA512 hash check for file.
##
## This switch is only valid when building with the `--head` command line flag.
##
## ### HEADERS
## A list of headers to append to the download request. This can be used for authentication during a download.
##
## Headers should be specified as "<header-name>: <header-value>".
##
## ## Notes
## The helper [`vcpkg_from_github`](vcpkg_from_github.md) should be used for downloading from GitHub projects.
##
## ## Examples
##
## * [apr](https://github.com/Microsoft/vcpkg/blob/master/ports/apr/portfile.cmake)
## * [fontconfig](https://github.com/Microsoft/vcpkg/blob/master/ports/fontconfig/portfile.cmake)
## * [freetype](https://github.com/Microsoft/vcpkg/blob/master/ports/freetype/portfile.cmake)
include(vcpkg_execute_in_download_mode)
function(vcpkg_download_distfile VAR)
set(options SKIP_SHA512 SILENT_EXIT QUIET)
set(oneValueArgs FILENAME SHA512)
set(multipleValuesArgs URLS HEADERS)
# parse parameters such that semicolons in options arguments to COMMAND don't get erased
cmake_parse_arguments(PARSE_ARGV 1 vcpkg_download_distfile "${options}" "${oneValueArgs}" "${multipleValuesArgs}")
if(NOT DEFINED vcpkg_download_distfile_URLS)
message(FATAL_ERROR "vcpkg_download_distfile requires a URLS argument.")
endif()
if(NOT DEFINED vcpkg_download_distfile_FILENAME)
message(FATAL_ERROR "vcpkg_download_distfile requires a FILENAME argument.")
endif()
if(NOT _VCPKG_INTERNAL_NO_HASH_CHECK)
if(NOT vcpkg_download_distfile_SKIP_SHA512 AND NOT DEFINED vcpkg_download_distfile_SHA512)
message(FATAL_ERROR "vcpkg_download_distfile requires a SHA512 argument. If you do not know the SHA512, add it as 'SHA512 0' and re-run this command.")
endif()
if(vcpkg_download_distfile_SKIP_SHA512 AND DEFINED vcpkg_download_distfile_SHA512)
message(FATAL_ERROR "vcpkg_download_distfile must not be passed both SHA512 and SKIP_SHA512.")
endif()
endif()
set(downloaded_file_path ${DOWNLOADS}/${vcpkg_download_distfile_FILENAME})
set(download_file_path_part "${DOWNLOADS}/temp/${vcpkg_download_distfile_FILENAME}")
# Works around issue #3399
if(IS_DIRECTORY "${DOWNLOADS}/temp")
# Delete "temp0" directory created by the old version of vcpkg
file(REMOVE_RECURSE "${DOWNLOADS}/temp0")
file(GLOB temp_files "${DOWNLOADS}/temp")
file(REMOVE_RECURSE ${temp_files})
else()
file(MAKE_DIRECTORY "${DOWNLOADS}/temp")
endif()
function(test_hash FILE_PATH FILE_KIND CUSTOM_ERROR_ADVICE)
if(_VCPKG_INTERNAL_NO_HASH_CHECK)
# When using the internal hash skip, do not output an explicit message.
return()
endif()
if(vcpkg_download_distfile_SKIP_SHA512)
message(STATUS "Skipping hash check for ${FILE_PATH}.")
return()
endif()
file(SHA512 ${FILE_PATH} FILE_HASH)
if(NOT FILE_HASH STREQUAL vcpkg_download_distfile_SHA512)
message(FATAL_ERROR
"\nFile does not have expected hash:\n"
" File path: [ ${FILE_PATH} ]\n"
" Expected hash: [ ${vcpkg_download_distfile_SHA512} ]\n"
" Actual hash: [ ${FILE_HASH} ]\n"
"${CUSTOM_ERROR_ADVICE}\n")
endif()
endfunction()
if(EXISTS "${downloaded_file_path}")
if(NOT vcpkg_download_distfile_QUIET)
message(STATUS "Using cached ${downloaded_file_path}")
endif()
test_hash("${downloaded_file_path}" "cached file" "Please delete the file and retry if this file should be downloaded again.")
else()
if(_VCPKG_NO_DOWNLOADS)
message(FATAL_ERROR "Downloads are disabled, but '${downloaded_file_path}' does not exist.")
endif()
# Tries to download the file.
list(GET vcpkg_download_distfile_URLS 0 SAMPLE_URL)
if(_VCPKG_DOWNLOAD_TOOL STREQUAL "ARIA2" AND NOT SAMPLE_URL MATCHES "aria2")
vcpkg_find_acquire_program("ARIA2")
message(STATUS "Downloading ${vcpkg_download_distfile_FILENAME}...")
if(vcpkg_download_distfile_HEADERS)
foreach(header ${vcpkg_download_distfile_HEADERS})
list(APPEND request_headers "--header=${header}")
endforeach()
endif()
vcpkg_execute_in_download_mode(
COMMAND ${ARIA2} ${vcpkg_download_distfile_URLS}
-o temp/${vcpkg_download_distfile_FILENAME}
-l download-${vcpkg_download_distfile_FILENAME}-detailed.log
${request_headers}
OUTPUT_FILE download-${vcpkg_download_distfile_FILENAME}-out.log
ERROR_FILE download-${vcpkg_download_distfile_FILENAME}-err.log
RESULT_VARIABLE error_code
WORKING_DIRECTORY ${DOWNLOADS}
)
if (NOT "${error_code}" STREQUAL "0")
message(STATUS
"Downloading ${vcpkg_download_distfile_FILENAME}... Failed.\n"
" Exit Code: ${error_code}\n"
" See logs for more information:\n"
" ${DOWNLOADS}/download-${vcpkg_download_distfile_FILENAME}-out.log\n"
" ${DOWNLOADS}/download-${vcpkg_download_distfile_FILENAME}-err.log\n"
" ${DOWNLOADS}/download-${vcpkg_download_distfile_FILENAME}-detailed.log\n"
)
set(download_success 0)
else()
file(REMOVE
${DOWNLOADS}/download-${vcpkg_download_distfile_FILENAME}-out.log
${DOWNLOADS}/download-${vcpkg_download_distfile_FILENAME}-err.log
${DOWNLOADS}/download-${vcpkg_download_distfile_FILENAME}-detailed.log
)
set(download_success 1)
endif()
else()
foreach(url IN LISTS vcpkg_download_distfile_URLS)
message(STATUS "Downloading ${url}...")
if(vcpkg_download_distfile_HEADERS)
foreach(header ${vcpkg_download_distfile_HEADERS})
list(APPEND request_headers HTTPHEADER ${header})
endforeach()
endif()
file(DOWNLOAD "${url}" "${download_file_path_part}" STATUS download_status ${request_headers})
list(GET download_status 0 status_code)
if (NOT "${status_code}" STREQUAL "0")
message(STATUS "Downloading ${url}... Failed. Status: ${download_status}")
set(download_success 0)
else()
set(download_success 1)
break()
endif()
endforeach(url)
endif()
if (NOT vcpkg_download_distfile_SILENT_EXIT)
if (NOT download_success)
message(FATAL_ERROR
" \n"
" Failed to download file.\n"
" If you use a proxy, please set the HTTPS_PROXY and HTTP_PROXY environment\n"
" variables to \"https://user:password@your-proxy-ip-address:port/\".\n"
" Otherwise, please submit an issue at https://github.com/Microsoft/vcpkg/issues\n")
else()
test_hash("${download_file_path_part}" "downloaded file" "The file may have been corrupted in transit. This can be caused by proxies. If you use a proxy, please set the HTTPS_PROXY and HTTP_PROXY environment variables to \"https://user:password@your-proxy-ip-address:port/\".\n")
get_filename_component(downloaded_file_dir "${downloaded_file_path}" DIRECTORY)
file(MAKE_DIRECTORY "${downloaded_file_dir}")
file(RENAME ${download_file_path_part} ${downloaded_file_path})
endif()
else()
if (NOT download_success)
message(WARNING
" \n"
" Failed to download file.\n")
else()
test_hash("${download_file_path_part}" "downloaded file" "The file may have been corrupted in transit. This can be caused by proxies. If you use a proxy, please set the HTTPS_PROXY and HTTP_PROXY environment variables to \"https://user:password@your-proxy-ip-address:port/\".\n")
get_filename_component(downloaded_file_dir "${downloaded_file_path}" DIRECTORY)
file(MAKE_DIRECTORY "${downloaded_file_dir}")
file(RENAME ${download_file_path_part} ${downloaded_file_path})
endif()
endif()
endif()
set(${VAR} ${downloaded_file_path} PARENT_SCOPE)
endfunction()