# 11.1 生成源代碼和二進制包
**NOTE**:*此示例代碼可以在 https://github.com/dev-cafe/cmake-cookbook/tree/v1.0/chapter-11/recipe-01 中找到。該示例在CMake 3.6版(或更高版本)中是有效的,并且已經在GNU/Linux、macOS和Windows上進行過測試。*
如果代碼是開源的,用戶將能夠下載項目的源代碼,并使用完全定制的CMake腳本自行構建。當然,打包操作也可以使用腳本完成,但是CPack提供了更簡單和可移植的替代方案。本示例將指導您創建一些包:
* **源代碼包**:可以將源代碼直接壓縮成歸檔文件,進行發送。用戶將不必為特定的版本控制系統操心。
* **二進制包**:工具將新構建的目標以打包的方式到歸檔文件中。這個功能非常有用,但可能不夠健壯,無法發布庫和可執行程序。
* **平臺原生的二進制安裝**:CPack能夠以許多不同的格式生成二進制安裝程序,因此可以將軟件發布到不同的平臺。我們將展示如何生成安裝程序:
* 基于Debian的GNU/Linux發行版的`.deb`格式: https://manpages.debian.org/unstable/dpkg-dev/deb.5.en.html
* 基于Red Hat的GNU/Linux發行版的`.rpm`格式: http://rpm.org/
* macOS包的`.dmg`格式: https://developer.apple.com/library/archive/documentation/CoreFoundation/Conceptual/CFBundles/BundleTypes/BundleTypes.html
* Windows的NSIS格式: http://nsis.sourceforge.net/Main_Page
## 準備工作
我們將使用第10章第3節的示例,項目樹由以下目錄和文件組成:
```shell
.
├── cmake
│ ├── coffee.icns
│ ├── Info.plist.in
│ └── messageConfig.cmake.in
├── CMakeCPack.cmake
├── CMakeLists.txt
├── INSTALL.md
├── LICENSE
├── src
│ ├── CMakeLists.txt
│ ├── hello-world.cpp
│ ├── Message.cpp
│ └── Message.hpp
└── tests
├── CMakeLists.txt
└── use_target
├── CMakeLists.txt
└── use_message.cpp
```
由于本示例的重點是使用CPack,所以不會討論源碼。我們只會在`CMakeCPack.cmake`中添加打包指令。此外,還添加了`INSTALL.md`和`LICENSE`文件:打包要求需要包含安裝說明和項目許可信息。
## 具體實施
讓我們看看需要添加到這個項目中的打包指令。我們將在` CMakeCPack.cmake `中收集它們,并在在`CMakeLists.txt`的末尾包含這個模塊`include(cmakecpackage.cmake)`:
1. 我們聲明包的名稱,與項目的名稱相同,因此我們使用`PROJECT_NAME`的CMake變量:
```cmake
set(CPACK_PACKAGE_NAME "${PROJECT_NAME}")
```
2. 聲明包的供應商:
```cmake
set(CPACK_PACKAGE_VENDOR "CMake Cookbook")
```
3. 打包的源代碼將包括一個描述文件。這是帶有安裝說明的純文本文件:
```cmake
set(CPACK_PACKAGE_DESCRIPTION_FILE "${PROJECT_SOURCE_DIR}/INSTALL.md")
```
4. 還添加了一個包的描述:
```cmake
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "message: a small messaging library")
```
5. 許可證文件也將包括在包中:
```cmake
set(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE")
```
6. 從發布包中安裝時,文件將放在`/opt/recipe-01`目錄下:
```cmake
set(CPACK_PACKAGING_INSTALL_PREFIX "/opt/${PROJECT_NAME}")
```
7. CPack所需的主要、次要和補丁版本:
```cmake
set(CPACK_PACKAGE_VERSION_MAJOR "${PROJECT_VERSION_MAJOR}")
set(CPACK_PACKAGE_VERSION_MINOR "${PROJECT_VERSION_MINOR}")
set(CPACK_PACKAGE_VERSION_PATCH "${PROJECT_VERSION_PATCH}")
```
8. 設置了在包裝的時候需要忽略的文件列表和目錄:
```cmake
set(CPACK_SOURCE_IGNORE_FILES "${PROJECT_BINARY_DIR};/.git/;.gitignore")
```
9. 列出了源代碼歸檔的打包生成器——在我們的例子中是`ZIP`,用于生成`.ZIP`歸檔,`TGZ`用于`.tar.gz`歸檔:
```cmake
set(CPACK_SOURCE_GENERATOR "ZIP;TGZ")
```
10. 我們還列出了二進制存檔生成器:
```cmake
set(CPACK_GENERATOR "ZIP;TGZ")
```
11. 現在也可聲明平臺原生二進制安裝程序,從DEB和RPM包生成器開始,不過只適用于GNU/Linux:
```cmake
if(UNIX)
if(CMAKE_SYSTEM_NAME MATCHES Linux)
list(APPEND CPACK_GENERATOR "DEB")
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "robertodr")
set(CPACK_DEBIAN_PACKAGE_SECTION "devel")
set(CPACK_DEBIAN_PACKAGE_DEPENDS "uuid-dev")
list(APPEND CPACK_GENERATOR "RPM")
set(CPACK_RPM_PACKAGE_RELEASE "1")
set(CPACK_RPM_PACKAGE_LICENSE "MIT")
set(CPACK_RPM_PACKAGE_REQUIRES "uuid-devel")
endif()
endif()
```
12. 如果我們在Windows上,我們會想要生成一個NSIS安裝程序:
```cmake
if(WIN32 OR MINGW)
list(APPEND CPACK_GENERATOR "NSIS")
set(CPACK_NSIS_PACKAGE_NAME "message")
set(CPACK_NSIS_CONTACT "robertdr")
set(CPACK_NSIS_ENABLE_UNINSTALL_BEFORE_INSTALL ON)
endif()
```
13. 另一方面,在macOS上,bundle包是我們的安裝程序的選擇:
```cmake
if(APPLE)
list(APPEND CPACK_GENERATOR "Bundle")
set(CPACK_BUNDLE_NAME "message")
configure_file(${PROJECT_SOURCE_DIR}/cmake/Info.plist.in Info.plist @ONLY)
set(CPACK_BUNDLE_PLIST ${CMAKE_CURRENT_BINARY_DIR}/Info.plist)
set(CPACK_BUNDLE_ICON ${PROJECT_SOURCE_DIR}/cmake/coffee.icns)
endif()
```
14. 我們在現有系統的包裝生成器上,向用戶打印一條信息:
```cmake
message(STATUS "CPack generators: ${CPACK_GENERATOR}")
```
15. 最后,我們包括了`CPack.cmake`標準模塊。這將向構建系統添加一個包和一個`package_source`目標:
```cmake
include(CPack)
```
現在來配置這個項目:
```shell
$ mkdir -p build
$ cd build
$ cmake ..
```
使用下面的命令,我們可以列出可用的目標(示例輸出是在使用Unix Makefile作為生成器的GNU/Linux系統上獲得的):
```shell
$ cmake --build . --target help
The following are some of the valid targets for this Makefile:
... all (the default if no target is provided)
... clean
... depend
... install/strip
... install
... package_source
... package
... install/local
... test
... list_install_components
... edit_cache
... rebuild_cache
... hello- world
... message
```
我們可以看到`package`和`package_source`目標是可用的。可以使用以下命令生成源包:
```shell
$ cmake --build . --target package_source
Run CPack packaging tool for source...
CPack: Create package using ZIP
CPack: Install projects
CPack: - Install directory: /home/user/cmake-cookbook/chapter-11/recipe-01/cxx-example
CPack: Create package
CPack: - package: /home/user/cmake-cookbook/chapter- 11/recipe-01/cxx-example/build/recipe-01-1.0.0-Source.zip generated.
CPack: Create package using TGZ
CPack: Install projects
CPack: - Install directory: /home/user/cmake-cookbook/chapter- 11/recipe-01/cxx-example
CPack: Create package
CPack: - package: /home/user/cmake-cookbook/chapter-11/recipe-01/cxx-example/build/recipe-01- 1.0.0-Source.tar.gz generated.
```
同樣,也可以構建二進制包:
```shell
$ cmake --build . --target package message-1.0.0-Linux.deb
```
例子中,最后得到了以下二進制包:
```shell
message-1.0.0-Linux.rpm
message-1.0.0-Linux.tar.gz
message-1.0.0-Linux.zip
```
## 工作原理
CPack可用于生成用于分發的包。生成構建系統時,我們在`CMakeCPack.cmake`中列出了CPack指令,用于在構建目錄下生成` CPackConfig.cmake`。當運行以`package`或`package_source`目標的CMake命令時,CPack會自動調用,參數是自動生成的配置文件。實際上,這兩個新目標是對CPack簡單規則的使用。與CMake一樣,CPack也有生成器的概念。CMake上下文中的生成器是用于生成本地構建腳本的工具,例如Unix Makefile或Visual Studio項目文件,而CPack上下文中的生成器是用于打包的工具。我們列出了這些變量,并對不同的平臺進行了特別的關注,為源包和二進制包定義了`CPACK_SOURCE_GENERATOR`和`CPACK_GENERATOR`變量。因此,`DEB`包生成器將調用`Debian`打包實用程序,而`TGZ`生成器將調用給定平臺上的歸檔工具。我們可以直接在`build`目錄中調用CPack,并選擇要與`-G`命令行選項一起使用的生成器。`RPM`包可以通過以下步驟生成:
```shell
$ cd build
$ cpack -G RPM
CPack: Create package using RPM
CPack: Install projects
CPack: - Run preinstall target for: recipe-01
CPack: - Install project: recipe-01
CPack: Create package
CPackRPM: Will use GENERATED spec file: /home/user/cmake-cookbook/chapter-11/recipe-01/cxx-example/build/_CPack_Packages/Linux/RPM/SPECS/recipe-01.spec
CPack: - package: /home/user/cmake-cookbook/chapter-11/recipe-01/cxx-example/build/recipe-01-1.0.0-Linux.rpm generated.
```
對于任何發行版,無論是源代碼還是二進制文件,我們只需要打包用戶需要的內容,因此整個構建目錄和其他與版本控制相關的文件,都必須從要打包的文件列表中排除。我們的例子中,排除列表使用下面的命令聲明:
```cmake
set(CPACK_SOURCE_IGNORE_FILES "${PROJECT_BINARY_DIR};/.git/;.gitignore")
```
我們還需要指定包的基本信息,例如:名稱、簡短描述和版本。這個信息是通過CMake變量設置的,當包含相應的模塊時,CMake變量被傳遞給CPack。
**NOTE**:*由于CMake 3.9中的`project()`命令接受`DESCRIPTION`字段,該字段帶有一個描述項目的短字符串。CMake將設置一個`PROJECT_DESCRIPTION`,可以用它來重置`CPACK_PACKAGE_DESCRIPTION_SUMMARY`。*
讓我們詳細看看,可以為示例項目生成的不同類型包的說明。
### 打包源碼
我們的示例中,決定對源存檔使用`TGZ`和`ZIP`生成器。這些文件將分別生成`.tar.gz`和`.zip`壓縮文件。我們可以檢查生成的`.tar.gz`文件的內容:
```shell
$ tar tzf recipe-01-1.0.0-Source.tar.gz
recipe-01-1.0.0-Source/opt/
recipe-01-1.0.0-Source/opt/recipe-01/
recipe-01-1.0.0-Source/opt/recipe-01/cmake/
recipe-01-1.0.0-Source/opt/recipe-01/cmake/coffee.icns
recipe-01-1.0.0-Source/opt/recipe-01/cmake/Info.plist.in
recipe-01-1.0.0-Source/opt/recipe-01/cmake/messageConfig.cmake.in
recipe-01-1.0.0-Source/opt/recipe-01/CMakeLists.txt
recipe-01-1.0.0-Source/opt/recipe-01/src/
recipe-01-1.0.0-Source/opt/recipe-01/src/Message.hpp
recipe-01-1.0.0-Source/opt/recipe-01/src/CMakeLists.txt
recipe-01-1.0.0-Source/opt/recipe-01/src/Message.cpp
recipe-01-1.0.0-Source/opt/recipe-01/src/hello-world.cpp
recipe-01-1.0.0-Source/opt/recipe-01/LICENSE
recipe-01-1.0.0-Source/opt/recipe-01/tests/
recipe-01-1.0.0-Source/opt/recipe-01/tests/CMakeLists.txt
recipe-01-1.0.0-Source/opt/recipe-01/tests/use_target/
recipe-01-1.0.0-Source/opt/recipe-01/tests/use_target/CMakeLists.txt
recipe-01-1.0.0-Source/opt/recipe-01/tests/use_target/use_message.cpp
recipe-01-1.0.0-Source/opt/recipe-01/INSTALL.md
```
與預期相同,只包含源碼樹的內容。注意`INSTALL.md `和`LICENSE`文件也包括在內,可以通過`CPACK_PACKAGE_DESCRIPTION_FILE`和`CPACK_RESOURCE_FILE_LICENSE`變量指定。
**NOTE**:*Visual Studio生成器無法解析`package_source`目標:https://gitlab.kitware.com/cmake/cmake/issues/13058。*
### 二進制包
創建二進制存檔時,CPack將打包`CMakeCPack.cmake`中描述的目標的內容。因此,在我們的示例中,hello-world可執行文件、消息動態庫以及相應的頭文件都將以`.tar.gz`和`.zip`的格式打包。此外,還將打包CMake配置文件。這對于需要鏈接到我們的庫的其他項目非常有用。包中使用的安裝目錄可能與從構建樹中安裝項目時使用的前綴不同,可以使用`CPACK_PACKAGING_INSTALL_PREFIX`變量來實現這一點。我們的示例中,我們將它設置為系統上的特定位置:`/opt/recipe-01`。
```shell
$ tar tzf recipe-01-1.0.0-Linux.tar.gz
recipe-01- 1.0.0-Linux/opt/
recipe-01-1.0.0-Linux/opt/recipe-01/
recipe-01-1.0.0- Linux/opt/recipe-01/bin/
recipe-01-1.0.0-Linux/opt/recipe-01/bin/hello- world
recipe-01-1.0.0-Linux/opt/recipe-01/share/
recipe-01-1.0.0- Linux/opt/recipe-01/share/cmake/
recipe-01-1.0.0-Linux/opt/recipe- 01/share/cmake/recipe-01/
recipe-01-1.0.0-Linux/opt/recipe- 01/share/cmake/recipe-01/messageConfig.cmake
recipe-01-1.0.0- Linux/opt/recipe-01/share/cmake/recipe-01/messageTargets-hello- world.cmake
recipe-01-1.0.0-Linux/opt/recipe-01/share/cmake/recipe- 01/messageConfigVersion.cmake
recipe-01-1.0.0-Linux/opt/recipe- 01/share/cmake/recipe-01/messageTargets-hello-world- release.cmake
recipe-01-1.0.0-Linux/opt/recipe-01/share/cmake/recipe- 01/messageTargets-release.cmake
recipe-01-1.0.0-Linux/opt/recipe- 01/share/cmake/recipe-01/messageTargets.cmake
recipe-01-1.0.0- Linux/opt/recipe-01/include/
recipe-01-1.0.0-Linux/opt/recipe- 01/include/message/
recipe-01-1.0.0-Linux/opt/recipe- 01/include/message/Message.hpp
recipe-01-1.0.0-Linux/opt/recipe- 01/include/message/messageExport.h
recipe-01-1.0.0-Linux/opt/recipe- 01/lib64/
recipe-01-1.0.0-Linux/opt/recipe- 01/lib64/libmessage.so
recipe-01-1.0.0-Linux/opt/recipe- 01/lib64/libmessage.so.1`
```
### 平臺原生的二進制安裝
我們希望每個平臺原生二進制安裝程序的配置略有不同。可以在單個`CMakeCPack.cmake`中使用CPack管理這些差異,就像例子中做的那樣。
對于GNU/Linux系統,配置了`DEB`和`RPM`生成器:
```cmake
if(UNIX)
if(CMAKE_SYSTEM_NAME MATCHES Linux)
list(APPEND CPACK_GENERATOR "DEB")
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "robertodr")
set(CPACK_DEBIAN_PACKAGE_SECTION "devel")
set(CPACK_DEBIAN_PACKAGE_DEPENDS "uuid-dev")
list(APPEND CPACK_GENERATOR "RPM")
set(CPACK_RPM_PACKAGE_RELEASE "1")
set(CPACK_RPM_PACKAGE_LICENSE "MIT")
set(CPACK_RPM_PACKAGE_REQUIRES "uuid-devel")
endif()
endif()
```
我們的示例依賴于UUID庫,`CPACK_DEBIAN_PACKAGE_DEPENDS`和`cpack_rpm_package_require`選項允許指定,包和數據庫中對其他包的依賴關系。可以使用dpkg和rpm程序分別分析生成的`.deb`和`.rpm`包的內容。
注意,`CPACK_PACKAGING_INSTALL_PREFIX`也會影響這些包生成器:我們的包將安裝到`/opt/recipe-01`。
CMake真正提供了跨平臺和可移植構建系統的支持。下面將使用Nullsoft腳本安裝系統(NSIS)創建一個安裝程序:
```cmake
if(WIN32 OR MINGW)
list(APPEND CPACK_GENERATOR "NSIS")
set(CPACK_NSIS_PACKAGE_NAME "message")
set(CPACK_NSIS_CONTACT "robertdr")
set(CPACK_NSIS_ENABLE_UNINSTALL_BEFORE_INSTALL ON)
endif()
```
如果在macOS上構建項目,將啟用`Bundle packager`:
```cmake
if(APPLE)
list(APPEND CPACK_GENERATOR "Bundle")
set(CPACK_BUNDLE_NAME "message")
configure_file(${PROJECT_SOURCE_DIR}/cmake/Info.plist.in Info.plist @ONLY)
set(CPACK_BUNDLE_PLIST ${CMAKE_CURRENT_BINARY_DIR}/Info.plist)
set(CPACK_BUNDLE_ICON ${PROJECT_SOURCE_DIR}/cmake/coffee.icns)
endif()
```
macOS的示例中,需要為包配置屬性列表文件,這是通過`configure_file`實現的。`Info.plist`的位置和包的圖標,這些都可以通過CPack的變量進行設置。
**NOTE**:*可以在這里閱讀,關于屬性列表格式的更多信息:https://en.wikipedia.org/wiki/Property_list*
## 更多信息
對` CMakeCPack.cmake `進行設置,要比列出CPack的配置選項簡單的多,我們可以將`CPACK_*`變量的每個生成器設置放在單獨的文件中,比如`CMakeCPackOptions.cmake`,并將這些設置包含到`CMakeCPack.cmake`使用`set(CPACK_PROJECT_CONFIG_FILE "${PROJECT_SOUsRCE_DIR}/CMakeCPackOptions.cmake")`將設置包含入` CMakeCPack.cmake`中。還可以在CMake時配置該文件,然后在CPack時包含該文件,這為配置多格式包生成器提供了一種簡潔的方法(參見https://cmake.org/cmake/help/v3.6/module/CPack.html )。
與CMake中的所有工具一樣,CPack功能強大、功能多樣,并且提供了更多的靈活性和選項。感興趣的讀者應該看官方文檔的命令行界面CPack (https://cmake.org/cmake/help/v3.6/manual/cpack.1.html )手冊頁,如何使用CPack生成器打包相關項目的更多細節(https://cmake.org/cmake/help/v3.6/module/CPack.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 留下評論——讓其他讀者知道你的想法