# 8.3 使用超級構建管理依賴項:Ⅱ.FFTW庫
**NOTE**:*此示例代碼可以在 https://github.com/dev-cafe/cmake-cookbook/tree/v1.0/chapter-8/recipe-03 中找到,其中有一個C示例。該示例在CMake 3.5版(或更高版本)中是有效的,并且已經在GNU/Linux、macOS和Windows上進行過測試。*
對于CMake支持的所有項目,超級構建模式可用于管理相當復雜的依賴關系。正如在前面的示例所演示的,CMake并不需要管理各種子項目。與前一個示例相反,這個示例中的外部子項目將是一個CMake項目,并將展示如何使用超級構建,下載、構建和安裝FFTW庫。FFTW是一個快速傅里葉變換庫,可在http://www.fftw.org 免費獲得。
我們項目的代碼`fftw_example.c`位于src子目錄中,它將計算源代碼中定義的函數的傅里葉變換。
## 準備工作
這個示例的目錄布局,是超級構建中非常常見的結構:
```shell
.
├── CMakeLists.txt
├── external
│ └── upstream
│ ├── CMakeLists.txt
│ └── fftw3
│ └── CMakeLists.txt
└── src
├── CMakeLists.txt
└── fftw_example.c
```
代碼`fftw_example.c`位于`src`子目錄中,它將調用傅里葉變換函數。
## 具體實施
從主`CMakeLists.txt`開始,這里將整個超級構建過程放在一起:
1. 聲明一個支持C99的項目:
```cmake
cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
project(recipe-03 LANGUAGES C)
set(CMAKE_C_STANDARD 99)
set(CMAKE_C_EXTENSIONS OFF)
set(CMAKE_C_STANDARD_REQUIRED ON)
```
2. 和上一個示例一樣,我們設置了`EP_BASE`目錄屬性和階段安裝目錄:
```cmake
set_property(DIRECTORY PROPERTY EP_BASE ${CMAKE_BINARY_DIR}/subprojects)
set(STAGED_INSTALL_PREFIX ${CMAKE_BINARY_DIR}/stage)
message(STATUS "${PROJECT_NAME} staged install: ${STAGED_INSTALL_PREFIX}")
```
3. 對FFTW的依賴關系在` external/upstream `子目錄中檢查,我們會將這個子目錄添加到構建系統中:
```cmake
add_subdirectory(external/upstream)
```
4. 包含` ExternalProject.cmake`模塊:
```cmake
include(ExternalProject)
```
5. 我們為`recipe-03_core`聲明了外部項目。這個項目的源代碼在`${CMAKE_CURRENT_LIST_DIR}/src`文件夾中。該項目設置為`FFTW3_DIR`選項,選擇正確的FFTW庫:
```cmake
ExternalProject_Add(${PROJECT_NAME}_core
DEPENDS
fftw3_external
SOURCE_DIR
${CMAKE_CURRENT_LIST_DIR}/src
CMAKE_ARGS
-DFFTW3_DIR=${FFTW3_DIR}
-DCMAKE_C_STANDARD=${CMAKE_C_STANDARD}
-DCMAKE_C_EXTENSIONS=${CMAKE_C_EXTENSIONS}
-DCMAKE_C_STANDARD_REQUIRED=${CMAKE_C_STANDARD_REQUIRED}
CMAKE_CACHE_ARGS
-DCMAKE_C_FLAGS:STRING=${CMAKE_C_FLAGS}
-DCMAKE_PREFIX_PATH:PATH=${CMAKE_PREFIX_PATH}
BUILD_ALWAYS
1
INSTALL_COMMAND
""
)
```
`external/upstream `子目錄還包含一個`CMakeLists.txt`:
這個文件中,添加`fftw3`文件夾作為構建系統中的另一個子目錄:
```cmake
add_subdirectory(fftw3)
```
` external/upstream/fftw3 `中的`CMakeLists.txt`負責處理依賴關系:
1. 首先,嘗試在系統上找到FFTW3庫。注意,我們配置`find_package`使用的參數:
```cmake
find_package(FFTW3 CONFIG QUIET)
```
2. 如果找到了庫,就可以導入目標`FFTW3::FFTW3`來鏈接它。我們向用戶打印一條消息,顯示庫的位置。我們添加一個虛擬`INTERFACE`庫`fftw3_external`。超級建設中,這需要正確地固定子項目之間的依賴樹:
```cmake
find_package(FFTW3 CONFIG QUIET)
if(FFTW3_FOUND)
get_property(_loc TARGET FFTW3::fftw3 PROPERTY LOCATION)
message(STATUS "Found FFTW3: ${_loc} (found version ${FFTW3_VERSION})")
add_library(fftw3_external INTERFACE) # dummy
else()
# this branch will be discussed below
endif()
```
3. 如果CMake無法找到預安裝版本的FFTW,我們將進入`else`分支。這個分支中,使用`ExternalProject_Add`下載、構建和安裝它。外部項目的名稱為`fftw3_external`。`fftw3_external`項目將從官方地址下載,下載完成后將使用MD5校驗和進行文件完整性檢查:
```cmake
message(STATUS "Suitable FFTW3 could not be located. Downloading and building!")
include(ExternalProject)
ExternalProject_Add(fftw3_external
URL
http://www.fftw.org/fftw-3.3.8.tar.gz
URL_HASH
MD5=8aac833c943d8e90d51b697b27d4384d
```
4. 禁用打印下載進程,并將更新命令定義為空:
```cmake
OWNLOAD_NO_PROGRESS
1
UPDATE_COMMAND
""
```
5. 配置、構建和安裝輸出將被記錄到一個文件中:
```cmake
LOG_CONFIGURE
1
LOG_BUILD
1
LOG_INSTALL
1
```
6. 將`fftw3_external`項目的安裝前綴設置為之前定義的`STAGED_INSTALL_PREFIX`目錄,并關閉FFTW3的測試套件構建:
```cmake
CMAKE_ARGS
-DCMAKE_INSTALL_PREFIX=${STAGED_INSTALL_PREFIX}
-DBUILD_TESTS=OFF
```
7. 如果在Windows上構建,通過生成器表達式設置`WITH_OUR_MALLOC`預處理器選項,并關閉`ExternalProject_Add`命令:
```cmake
CMAKE_CACHE_ARGS
-DCMAKE_C_FLAGS:STRING=$<$<BOOL:WIN32>:-DWITH_OUR_MALLOC>
)
```
8. 最后,定義`FFTW3_DIR`變量并緩存它。CMake將使用該變量作為`FFTW3::FFTW3`目標的搜索目錄:
```cmake
include(GNUInstallDirs)
set(
FFTW3_DIR ${STAGED_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/cmake/fftw3
CACHE PATH "Path to internally built FFTW3Config.cmake"
FORCE
)
```
`src`文件夾中的CMakeLists.txt相當簡潔:
1. 同樣在這個文件中,我們聲明了一個C項目:
```cmake
cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
project(recipe-03_core LANGUAGES C)
```
2. 使用`find_package`來檢測FFTW庫,再次使用配置檢測模式:
```cmake
find_package(FFTW3 CONFIG REQUIRED)
get_property(_loc TARGET FFTW3::fftw3 PROPERTY LOCATION)
message(STATUS "Found FFTW3: ${_loc} (found version ${FFTW3_VERSION})")
```
3. 將`fftw_example.c`源文件添加到可執行目標`fftw_example`:
```cmake
add_executable(fftw_example fftw_example.c)
```
4. 為可執行目標設置鏈接庫:
```cmake
target_link_libraries(fftw_example
PRIVATE
FFTW3::fftw3
)
```
## 工作原理
本示例演示了如何下載、構建和安裝由CMake管理其構建系統的外部項目。與前一個示例(必須使用自定義構建系統)相反,這個超級構建設置相當簡潔。需要注意的是,使用`find_package`命令了配置選項;這說明CMake首先查找`FFTW3Config.cmake`,以定位FFTW3庫,將庫導出為第三方項目獲取的目標。目標包含庫的版本、配置和位置,即關于如何配置和構建目標的完整信息。如果系統上沒有安裝庫,我們需要聲明` FFTW3Config.cmake`文件的位置。這可以通過設置`FFTW3_DIR`變量來實現。這是`external/upstream/fftw3/CMakeLists.txt`文件中的最后一步。使用` GNUInstallDirs.cmake`模塊,我們將`FFTW3_DIR`設置為緩存變量,以便稍后在超級構建中使用。
**TIPS**:*配置項目時將`CMAKE_DISABLE_FIND_PACKAGE_FFTW3`設置為`ON`,將跳過對FFTW庫的檢測,并始終執行超級構建。參考:https://cmake.org/cmake/help/v3.5/variable/CMAKE_DISABLE_FIND_PACKAGE_PackageName.html*
- Introduction
- 前言
- 第0章 配置環境
- 0.1 獲取代碼
- 0.2 Docker鏡像
- 0.3 安裝必要的軟件
- 0.4 測試環境
- 0.5 上報問題并提出改進建議
- 第1章 從可執行文件到庫
- 1.1 將單個源文件編譯為可執行文件
- 1.2 切換生成器
- 1.3 構建和鏈接靜態庫和動態庫
- 1.4 用條件句控制編譯
- 1.5 向用戶顯示選項
- 1.6 指定編譯器
- 1.7 切換構建類型
- 1.8 設置編譯器選項
- 1.9 為語言設定標準
- 1.10 使用控制流
- 第2章 檢測環境
- 2.1 檢測操作系統
- 2.2 處理與平臺相關的源代碼
- 2.3 處理與編譯器相關的源代碼
- 2.4 檢測處理器體系結構
- 2.5 檢測處理器指令集
- 2.6 為Eigen庫使能向量化
- 第3章 檢測外部庫和程序
- 3.1 檢測Python解釋器
- 3.2 檢測Python庫
- 3.3 檢測Python模塊和包
- 3.4 檢測BLAS和LAPACK數學庫
- 3.5 檢測OpenMP的并行環境
- 3.6 檢測MPI的并行環境
- 3.7 檢測Eigen庫
- 3.8 檢測Boost庫
- 3.9 檢測外部庫:Ⅰ. 使用pkg-config
- 3.10 檢測外部庫:Ⅱ. 自定義find模塊
- 第4章 創建和運行測試
- 4.1 創建一個簡單的單元測試
- 4.2 使用Catch2庫進行單元測試
- 4.3 使用Google Test庫進行單元測試
- 4.4 使用Boost Test進行單元測試
- 4.5 使用動態分析來檢測內存缺陷
- 4.6 預期測試失敗
- 4.7 使用超時測試運行時間過長的測試
- 4.8 并行測試
- 4.9 運行測試子集
- 4.10 使用測試固件
- 第5章 配置時和構建時的操作
- 5.1 使用平臺無關的文件操作
- 5.2 配置時運行自定義命令
- 5.3 構建時運行自定義命令:Ⅰ. 使用add_custom_command
- 5.4 構建時運行自定義命令:Ⅱ. 使用add_custom_target
- 5.5 構建時為特定目標運行自定義命令
- 5.6 探究編譯和鏈接命令
- 5.7 探究編譯器標志命令
- 5.8 探究可執行命令
- 5.9 使用生成器表達式微調配置和編譯
- 第6章 生成源碼
- 6.1 配置時生成源碼
- 6.2 使用Python在配置時生成源碼
- 6.3 構建時使用Python生成源碼
- 6.4 記錄項目版本信息以便報告
- 6.5 從文件中記錄項目版本
- 6.6 配置時記錄Git Hash值
- 6.7 構建時記錄Git Hash值
- 第7章 構建項目
- 7.1 使用函數和宏重用代碼
- 7.2 將CMake源代碼分成模塊
- 7.3 編寫函數來測試和設置編譯器標志
- 7.4 用指定參數定義函數或宏
- 7.5 重新定義函數和宏
- 7.6 使用廢棄函數、宏和變量
- 7.7 add_subdirectory的限定范圍
- 7.8 使用target_sources避免全局變量
- 7.9 組織Fortran項目
- 第8章 超級構建模式
- 8.1 使用超級構建模式
- 8.2 使用超級構建管理依賴項:Ⅰ.Boost庫
- 8.3 使用超級構建管理依賴項:Ⅱ.FFTW庫
- 8.4 使用超級構建管理依賴項:Ⅲ.Google Test框架
- 8.5 使用超級構建支持項目
- 第9章 語言混合項目
- 9.1 使用C/C++庫構建Fortran項目
- 9.2 使用Fortran庫構建C/C++項目
- 9.3 使用Cython構建C++和Python項目
- 9.4 使用Boost.Python構建C++和Python項目
- 9.5 使用pybind11構建C++和Python項目
- 9.6 使用Python CFFI混合C,C++,Fortran和Python
- 第10章 編寫安裝程序
- 10.1 安裝項目
- 10.2 生成輸出頭文件
- 10.3 輸出目標
- 10.4 安裝超級構建
- 第11章 打包項目
- 11.1 生成源代碼和二進制包
- 11.2 通過PyPI發布使用CMake/pybind11構建的C++/Python項目
- 11.3 通過PyPI發布使用CMake/CFFI構建C/Fortran/Python項目
- 11.4 以Conda包的形式發布一個簡單的項目
- 11.5 將Conda包作為依賴項發布給項目
- 第12章 構建文檔
- 12.1 使用Doxygen構建文檔
- 12.2 使用Sphinx構建文檔
- 12.3 結合Doxygen和Sphinx
- 第13章 選擇生成器和交叉編譯
- 13.1 使用CMake構建Visual Studio 2017項目
- 13.2 交叉編譯hello world示例
- 13.3 使用OpenMP并行化交叉編譯Windows二進制文件
- 第14章 測試面板
- 14.1 將測試部署到CDash
- 14.2 CDash顯示測試覆蓋率
- 14.3 使用AddressSanifier向CDash報告內存缺陷
- 14.4 使用ThreadSaniiser向CDash報告數據爭用
- 第15章 使用CMake構建已有項目
- 15.1 如何開始遷移項目
- 15.2 生成文件并編寫平臺檢查
- 15.3 檢測所需的鏈接和依賴關系
- 15.4 復制編譯標志
- 15.5 移植測試
- 15.6 移植安裝目標
- 15.7 進一步遷移的措施
- 15.8 項目轉換為CMake的常見問題
- 第16章 可能感興趣的書
- 16.1 留下評論——讓其他讀者知道你的想法