今天补下之前在Post not found: 【CMake系列】(三)ExternalProject-实践 【CMake系列】 (三)ExternalProject-实践留下的坑:如何下载第三方依赖。
理清需求
由于大家的需求很可能是不一致的,这里选一个比较通用的需求:下载第三方依赖压缩包,于是我们就需要下载压缩包文件到本地,验证文件签名,然后解压到指定目录。
CMake 提供的命令
我们要用到主要有以下两个命令:
实现
接下来我们一步步把功能实现。
下载功能
1 2 3 4 5 6 7 8 9
| function(download_file url filename)
message(STATUS "Download to ${filename} ...")
file(DOWNLOAD ${url} ${filename})
endfunction()
|
这便是实现了一个最简单的下载函数,我们直接传入链接地址以及文件名即可下载 download_file('http://example.com/1.zip', '2.zip')
。
接下来,我们开始添油加醋,慢慢实现自己的需求。
文件签名验证
1 2 3 4 5 6 7 8 9
| function(download_file_with_hash url filename hash_type hash)
message(STATUS "Download to ${filename} ...")
file(DOWNLOAD ${url} ${filename} EXPECTED_HASH ${hash_type}=${hash})
endfunction()
|
于是,调用方式变为 download_file_with_hash('http://example.com/1.zip', '2.zip', 'SHA1', 'xxxxxxxxxxxxxxx')
。
解压文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| function(extract_file filename extract_dir)
message(STATUS "Extract to ${extract_dir} ...")
set(temp_dir ${CMAKE_BINARY_DIR}/tmp_for_extract.dir)
if(EXISTS ${temp_dir})
file(REMOVE_RECURSE ${temp_dir})
endif()
file(MAKE_DIRECTORY ${temp_dir})
execute_process(COMMAND ${CMAKE_COMMAND} -E tar -xf ${filename}
WORKING_DIRECTORY ${temp_dir})
file(GLOB contents "${temp_dir}/*")
list(LENGTH contents n)
if(NOT n EQUAL 1 OR NOT IS_DIRECTORY "${contents}")
set(contents "${temp_dir}")
endif()
get_filename_component(contents ${contents} ABSOLUTE)
file(INSTALL "${contents}/" DESTINATION ${extract_dir})
file(REMOVE_RECURSE ${temp_dir})
endfunction()
|
下载后解压
1 2 3 4 5
| download_file('http://example.com/1.zip', '2.zip', 'SHA1', 'xxxxxxxxxxxxxxx')
extract_file('2.zip', '/path/to/install')
|
是不是很简单?现在我们加入更多的功能。
文件缓存
很多时候,如果下载的文件存在,我们只需要验证它的签名即可,即我们不用重复下载。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| if(EXISTS ${filename})
file(${hash_type} ${filename} _ACTUAL_CHKSUM)
if(NOT (${hash} STREQUAL ${_ACTUAL_CHKSUM}))
message(STATUS "Expect ${DAE_HASH_TYPE}=${_EXPECT_HASH}")
message(STATUS "Actual ${DAE_HASH_TYPE}=${_ACTUAL_CHKSUM}")
message(WARNING "File hash mismatch, remove & retry ...")
file(REMOVE ${filename})
download_file_with_hash(${url} ${filename} ${hash_type} ${hash})
else()
message(STATUS "Using exists local file ${filename}")
endif()
else()
download_file_with_hash(${url} ${filename} ${hash_type} ${hash})
endif()
|
参数解析
最后,我们将这个过程封装成一个单独的函数,并且加上参数解析。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
| function(download_and_extract)
set(options REMOVE_EXTRACT_DIR_IF_EXISTS)
set(oneValueArgs DESTINATION RENAME)
set(multiValueArgs)
set(oneValueArgs URL FILENAME HASH_TYPE HASH EXTRACT_DIR)
cmake_parse_arguments(DAE "${options}" "${oneValueArgs}" "${multiValueArgs}"
${ARGN})
if(NOT DEFINED DAE_URL)
message(FATAL_ERROR "Missing URL")
endif()
if(NOT DEFINED DAE_FILENAME)
message(FATAL_ERROR "Missing FILENAME")
endif()
if(NOT DEFINED DAE_HASH_TYPE)
message(FATAL_ERROR "Missing HASH_TYPE")
endif()
if(NOT DEFINED DAE_HASH)
message(FATAL_ERROR "Missing HASH")
endif()
if(NOT DEFINED DAE_EXTRACT_DIR)
message(FATAL_ERROR "Missing EXTRACT_DIR")
endif()
if(EXISTS ${DAE_EXTRACT_DIR})
if(DAE_REMOVE_EXTRACT_DIR_IF_EXISTS)
message(STATUS "${DAE_EXTRACT_DIR} already exists, removing...")
file(REMOVE_RECURSE ${DAE_EXTRACT_DIR})
else()
message(
STATUS "${DAE_EXTRACT_DIR} already exists, skip download & extract")
return()
endif()
endif()
if(EXISTS ${DAE_FILENAME})
file(${DAE_HASH_TYPE} ${DAE_FILENAME} _ACTUAL_CHKSUM)
if(NOT (${_EXPECT_HASH} STREQUAL ${_ACTUAL_CHKSUM}))
message(STATUS "Expect ${DAE_HASH_TYPE}=${_EXPECT_HASH}")
message(STATUS "Actual ${DAE_HASH_TYPE}=${_ACTUAL_CHKSUM}")
message(WARNING "File hash mismatch, remove & retry ...")
file(REMOVE ${DAE_FILENAME})
download_file_with_hash(${DAE_URL} ${DAE_FILENAME} ${DAE_HASH_TYPE}
${_EXPECT_HASH})
else()
message(STATUS "Using exists local file ${DAE_FILENAME}")
endif()
else()
download_file_with_hash(${DAE_URL} ${DAE_FILENAME} ${DAE_HASH_TYPE}
${_EXPECT_HASH})
endif()
extract_file(${DAE_FILENAME} ${DAE_EXTRACT_DIR})
endfunction()
|
于是,一个完整的文件下载解压函数就完成了,我们可以在项目中,这样使用自己实现的函数:
1 2 3 4 5 6 7 8 9 10 11 12 13
| download_and_extract(
URL https://example.com/1.tar.gz
FILENAME /tmp/1.tar.gz
HASH_TYPE SHA1
HASH xxxxxxxx
EXTRACT_DIR /tmp/example_dir)
|