今天补下之前在Post not found: 【CMake系列】(三)ExternalProject-实践 【CMake系列】 (三)ExternalProject-实践留下的坑:如何下载第三方依赖。
理清需求
由于大家的需求很可能是不一致的,这里选一个比较通用的需求:下载第三方依赖压缩包,于是我们就需要下载压缩包文件到本地,验证文件签名,然后解压到指定目录。
CMake 提供的命令
我们要用到主要有以下两个命令:
实现
接下来我们一步步把功能实现。
下载功能
| 12
 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')。
接下来,我们开始添油加醋,慢慢实现自己的需求。
文件签名验证
| 12
 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')。
解压文件
| 12
 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()
 
 
 | 
下载后解压
| 12
 3
 4
 5
 
 | download_file('http://example.com/1.zip', '2.zip', 'SHA1', 'xxxxxxxxxxxxxxx')
 
 extract_file('2.zip', '/path/to/install')
 
 
 | 
是不是很简单?现在我们加入更多的功能。
文件缓存
很多时候,如果下载的文件存在,我们只需要验证它的签名即可,即我们不用重复下载。
| 12
 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()
 
 
 | 
参数解析
最后,我们将这个过程封装成一个单独的函数,并且加上参数解析。
| 12
 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()
 
 
 | 
于是,一个完整的文件下载解压函数就完成了,我们可以在项目中,这样使用自己实现的函数:
| 12
 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)
 
 
 |