從最開始的 Configure, Build, Test 到現在的 Package, 我們終於來到了最後階段啦!
Day 21 的範例中, 我們已經能讓 CMake 幫我們安裝 targets 和 headers 到 FHS 對應的路徑了, 還缺什麼呢?
沒錯, 雖然現在已經可以用 cmake --install
安裝了, 但是我們總不能讓一般使用者去下 CMake 指令吧? 這時就需要將專案打包起來, 將打包後的檔案 (installer or archive) 提供給使用者, 就能夠用比較無腦的方式一建安裝了!
如果大家有看過甚至下載一些 open source 專案, 就會發現大多作者都會提供幾個安裝檔, 比如 Day 30 會介紹的 pytorch 就提供了一個 archive pytorch-v2.0.1.tar.gz
(版本可能和現在的不同)
所以, 今天會來介紹 如何打包 我們的專案, 以 CPack 為例, 介紹如何包出最簡單的 TGZ 檔, 也會介紹其他像是 RPM, DEB, FreeBSD 的格式, 至於給 Windows 用的安裝檔 (ex. InnoSetup, NSIS, WIX) 會在 Day 28 介紹, 那我們就開始吧!
Package tools 可以大致分成 archives, UI installer, non-UI installer, CMake 也支援支援產生 json 檔讓 package manager (ex. NuGet) 能安裝, 就可以擺脫 CPack
TGZ (or .tar.gz
) 是 archive 的一種, 通常結尾有 z
的檔案格式都是壓縮檔, 區別只在於是用甚麼工具壓縮而已, 所以像是 TBZ2, 7Z, TXZ 等等都是 archive
CPack 也支援 SFX (or self-extracting archive), 有關 SFX 的介紹請見 [wiki](https://en.wikipedia.org/wiki/Self-extracting_archive#:~:text=A%20self%2Dextracting%20archive%20(SFX,be%20already%20installed%20on%20the)
簡而言之, SFX 是一種可執行檔, 其內容包含 壓縮後的所有檔案, 所以他也是個壓縮檔, 但是能被 os 看懂並執行, 而不需要先解壓縮
通常 archive 都會有一個共同的 root directory, 所以我們可以設定 CPACK_PACKAGE_FILE_NAEM
來指定該路徑
set(CPACK_COMPONENT_INCLUDE_TOPLEVEL_DIRECTORY YES)
但是為了避免其他 generator 套用到這個設定, 最好加上一些判斷, 只有 archive generator 才會使用該設定
if(CPACK_GENERATOR MATCHES "^(7Z|TBZ2|TGZ|TXZ|TZ|TZST|ZIP)$")
set(CPACK_COMPONENT_INCLUDE_TOPLEVEL_DIRECTORY YES)
endif()
CPack 幫助我們將各種 package tools 的功能抽象出來, 並保留原有的功能給對應的 tools和 CMake 一樣, CPack 也有 generator 可以用, 不過和 CMake generator 產生 build tools 對應的 build scripts 不同, CPack generator 是產生 package tools 對應的 package scripts, 我們用 cpack --help
來看看都有什麼功能
$ cpack --help
Usage
cpack [options]
Options
-G <generators> = Override/define CPACK_GENERATOR
-C <Configuration> = Specify the project configuration
-D <var>=<value> = Set a CPack variable.
--config <configFile> = Specify the config file.
-V,--verbose = Enable verbose output
--trace = Put underlying cmake scripts in trace mode.
--trace-expand = Put underlying cmake scripts in expanded
trace mode.
--debug = Enable debug output (for CPack developers)
-P <packageName> = Override/define CPACK_PACKAGE_NAME
-R <packageVersion> = Override/define CPACK_PACKAGE_VERSION
-B <packageDirectory> = Override/define CPACK_PACKAGE_DIRECTORY
--vendor <vendorName> = Override/define CPACK_PACKAGE_VENDOR
--preset = Read arguments from a package preset
--list-presets = List available package presets
-h,-H,--help,-help,-usage,/? = Print usage information and exit.
--version,-version,/V [<file>]
= Print version number and exit.
--help-full [<file>] = Print all help manuals and exit.
--help-manual <man> [<file>] = Print one help manual and exit.
--help-manual-list [<file>] = List help manuals available and exit.
--help-command <cmd> [<file>]= Print help for one command and exit.
--help-command-list [<file>] = List commands with help available and exit.
--help-commands [<file>] = Print cmake-commands manual and exit.
--help-module <mod> [<file>] = Print help for one module and exit.
--help-module-list [<file>] = List modules with help available and exit.
--help-modules [<file>] = Print cmake-modules manual and exit.
--help-policy <cmp> [<file>] = Print help for one policy and exit.
--help-policy-list [<file>] = List policies with help available and exit.
--help-policies [<file>] = Print cmake-policies manual and exit.
--help-property <prop> [<file>]
= Print help for one property and exit.
--help-property-list [<file>]= List properties with help available and
exit.
--help-properties [<file>] = Print cmake-properties manual and exit.
--help-variable var [<file>] = Print help for one variable and exit.
--help-variable-list [<file>]= List variables with help available and exit.
--help-variables [<file>] = Print cmake-variables manual and exit.
Generators
7Z = 7-Zip file format
Bundle = Mac OSX bundle
DragNDrop = Mac OSX Drag And Drop
External = CPack External packages
IFW = Qt Installer Framework
NSIS = Null Soft Installer
NSIS64 = Null Soft Installer (64-bit)
NuGet = NuGet packages
STGZ = Self extracting Tar GZip compression
TBZ2 = Tar BZip2 compression
TGZ = Tar GZip compression
TXZ = Tar XZ compression
TZ = Tar Compress compression
TZST = Tar Zstandard compression
ZIP = ZIP file format
productbuild = Mac OSX pkg
Generators
section 可以看到 CPack 支援的所有 package scripts generators, 今天會介紹其中的 TGZ
-G
和 -C
-G
-C
使用 CPack
module 時 (include(CPack)
), CPack
會自動產生 CPackConfig.cmake
和 CPackSourceConfig.cmake
到 PROJECT_BINARY_DIR
, 前者是用來打包 binaries 的設定, 後者則是打包 source files 的設定 (ex. archive), cpack
就會自己去讀 CPackSourceConfig.cmake
config 並產生 package
target, 我們就可以用 cmake --build build --target package
指令來打包了
如果自己專案會被別人使用的話, 最好不要產生該檔案, 否則該 config 就會在別人專案的頂端被建立出來, 所以建議先判斷執行 cmake
的地方和 include(CPack)
的位置是否相同
if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
include(CPack)
endif()
並且, 在使用 CPack
module 前, 建議先設定以下幾個變數
CPACK_PACKAGE_NAME
CPACK_PACKAGE_VERSION_MAJOR
CPACK_PACKAGE_VERSION_MINOR
CPACK_PACKAGE_VERSION_PATCH
CPACK_PACKAGE_INSTALL_DIRECTORY
CPACK_VERBATIM_VARIABLES
CPackConfig.cmake
的內容CPACK_RESOURCE_*
系列
CPACK_GENERATOR
先來看看今天的專案架構, 我在 Day 21 的專案基礎上增加了 packaging
subdirectory
packaging/CMakeLists.txt
set(CPACK_PACKAGE_NAME ${PROJECT_NAME})
set(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR})
set(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR})
set(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH})
set(CPACK_PACKAGE_INSTALL_DIRECTORY ${PROJECT_NAME})
set(CPACK_VERBATIM_VARIABLES YES)
if(NOT CPACK_GENERATOR)
set(CPACK_GENERATOR TGZ)
message(STATUS "CPack: generator set to ${CPACK_GENERATOR}")
endif()
include(CPack)
並且這次會將 binary 也打包進來, 所以需要安裝 Main target, 故在頂端 CMakeLists.txt
就 include GNUInstallDirs
並且加入 packaging
subdirectory 初始化 CPack 設定CMakeLists.txt
cmake_minimum_required(VERSION 3.25)
project(Day21Example
LANGUAGES CXX
)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_VISIBILITY_PRESET hidden)
set(CMAKE_VISIBILITY_INLINES_HIDDEN TRUE)
include(GNUInstallDirs)
add_subdirectory(src)
if(PROJECT_IS_TOP_LEVEL)
add_subdirectory(packaging)
endif()
先產生 build scripts
$ cmake -S . -B build
然後我們就可以來產生安裝檔啦!
$ cmake --build build --target package
[ 16%] Building CXX object src/lib/CMakeFiles/StaticLib.dir/static_lib.cpp.o
[ 33%] Linking CXX static library libStaticLib.a
[ 33%] Built target StaticLib
[ 50%] Building CXX object src/lib/CMakeFiles/SharedLib.dir/shared_lib.cpp.o
[ 66%] Linking CXX shared library libSharedLib.so
[ 66%] Built target SharedLib
[ 83%] Building CXX object src/CMakeFiles/Main.dir/main.cpp.o
[100%] Linking CXX executable Main
[100%] Built target Main
Run CPack packaging tool...
CPack: Create package using TGZ
CPack: Install projects
CPack: - Run preinstall target for: Day21Example
CPack: - Install project: Day21Example []
CPack: Create package
CPack: - package: /content/cmake-example/build/Day21Example-0.1.1-Linux.tar.gz generated.
或是用 cpack
也行
$ cpack --config build/CPackConfig.cmake -C Debug
CPack: Create package using TGZ
CPack: Install projects
CPack: - Run preinstall target for: Day21Example
CPack: - Install project: Day21Example [Debug]
CPack: Create package
CPack: - package: /content/cmake-example/Day21Example-0.1.1-Linux.tar.gz generated.
可以看到在 build
底下多了 Day21Example-0.1.1-Linux.tar.gz
我們將他解壓縮到上層資料夾
tar xvf build/Day21Example-0.1.1-Linux.tar.gz -C ..
然後就可以看到我們安裝的 bin
, include
, lib
已經出現了, 代表我們打包成功啦🎉🎉🎉
下一篇, 讓我們像拉 open source project 一樣, link 自己的專案試試吧