cmake_minimum_required(VERSION 3.11) project(DownloadFile) cmake_policy(SET CMP0140 NEW) function(check_file_reachable url) file(DOWNLOAD "${url}" RANGE_END 10 TIMEOUT 2 INACTIVITY_TIMEOUT 1 STATUS download_status) list(GET download_status 0 download_status_code) if("${download_status_code}" STREQUAL "0") set(file_reachable "1") else() set(file_reachable "0") endif() return(PROPAGATE file_reachable) # File is reachable endfunction() function(download_file_with_hash_check url dest) set(options REQUIRED) set(oneValueArgs URL DEST HASH_URL) cmake_parse_arguments(PARSE_ARGV 0 ARG "${options}" "${oneValueArgs}" "") if(ARG_URL) set(url "${ARG_URL}") endif() if(ARG_DEST) set(dest "${ARG_DEST}") endif() set(hash_url "${url}.hash") if(ARG_HASH_URL) set(hash_url "${ARG_HASH_URL}") endif() message(STATUS "${hash_url}") check_file_reachable("${url}") if("${file_reachable}" STREQUAL "0") if(ARG_REQUIRED) if(NOT EXISTS "${dest}") message(FATAL_ERROR "File ${dest} could not be downloaded and is not present, even though it was marked as REQUIRED.") endif() endif() return() endif() set(hash_file "${dest}.hash") set(hash_file_temp "${hash_file}.temp") # Download the hash file (if available) file(DOWNLOAD "${hash_url}" "${hash_file_temp}" STATUS download_status TIMEOUT 60) list(GET download_status 0 download_status_code) if(NOT "${download_status_code}" STREQUAL "0") file(REMOVE "${hash_file_temp}") # Hash file is not available, just check if the file exists if(EXISTS "${dest}") message(STATUS "File ${dest} already exists, no hash file found. Skipping download.") return() else() message(STATUS "File ${dest} not found, downloading without hash check.") file(DOWNLOAD "${url}" "${dest}" SHOW_PROGRESS TIMEOUT 60) return() endif() endif() file(READ "${hash_file_temp}" expected_hash) string(STRIP "${expected_hash}" expected_hash) if(NOT "${expected_hash}" STREQUAL "") file(RENAME "${hash_file_temp}" "${hash_file}") endif() # Hash file downloaded, now read the hash file(READ "${hash_file}" expected_hash) string(STRIP "${expected_hash}" expected_hash) # Remove whitespace around hash if(EXISTS "${dest}") file(SHA256 "${dest}" actual_hash) if("${expected_hash}" STREQUAL "${actual_hash}") message(STATUS "File ${dest} already matches the hash. Skipping download.") return() else() message(WARNING "Hash mismatch for ${dest}. Downloading the file again.") endif() else() message(STATUS "File ${dest} not found. Downloading.") endif() # Download the actual file file(DOWNLOAD "${url}" "${dest}" SHOW_PROGRESS TIMEOUT 60) # Recheck the hash after downloading file(SHA256 "${dest}" actual_hash) if(NOT "${expected_hash}" STREQUAL "${actual_hash}") message(FATAL_ERROR "Downloaded file ${dest} hash mismatch. Expected: ${expected_hash}, got: ${actual_hash}") else() message(STATUS "File ${dest} downloaded and verified successfully.") endif() endfunction() download_file_with_hash_check(URL "http://127.0.0.1:8080/random_file.bin" DEST "${CMAKE_BINARY_DIR}/random_file.bin" REQUIRED)