iT邦幫忙

2023 iThome 鐵人賽

DAY 22
0

本日內容

  • Install Basics
  • CPack
  • Package to TGZ
  • 預告

Day 22 - Colab

從最開始的 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 (版本可能和現在的不同)
https://ithelp.ithome.com.tw/upload/images/20230922/20161950PYeBFGtUpL.png

所以, 今天會來介紹 如何打包 我們的專案, 以 CPack 為例, 介紹如何包出最簡單的 TGZ 檔, 也會介紹其他像是 RPM, DEB, FreeBSD 的格式, 至於給 Windows 用的安裝檔 (ex. InnoSetup, NSIS, WIX) 會在 Day 28 介紹, 那我們就開始吧!

Install Basics

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

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
  • Options 的選項很多, 今天主要會講最重要的 -G-C
  • -G
    • 指定 generator
  • -C
    • 如果是用 Multi-Config Package Tool, 可以指定要用哪個 package type 打包

使用 CPack module 時 (include(CPack)), CPack 會自動產生 CPackConfig.cmakeCPackSourceConfig.cmakePROJECT_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
    • 建議不要包含 version, 避免後續無法升級, 變成安裝新版本, 造成新舊版本共存
  • CPACK_VERBATIM_VARIABLES
    • 讓 CMake escape CPackConfig.cmake 的內容
  • CPACK_RESOURCE_* 系列
    • 給 UI installer 顯示訊息用
  • CPACK_GENERATOR

Package to TGZ

先來看看今天的專案架構, 我在 Day 21 的專案基礎上增加了 packaging subdirectory
https://ithelp.ithome.com.tw/upload/images/20230922/20161950lEDrT4nlPR.png

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 已經出現了, 代表我們打包成功啦🎉🎉🎉
https://ithelp.ithome.com.tw/upload/images/20230922/20161950W7Y91ZJNeu.png

預告

下一篇, 讓我們像拉 open source project 一樣, link 自己的專案試試吧


上一篇
[Day 21] Relocatable project
下一篇
[Day 23] 讓別人 Link 我的 Project
系列文
30 天 CMake 跨平台之旅30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言