CMake 入門/輸出位置與安裝
指定輸出目錄
編輯在前面的例子裏我們並沒有特別指定輸出檔的位置,因此檔案會分佈在 binary tree 當中,而 binary tree 的架構則和 source tree 相同。
這裏先介紹 CMake 對於檔案的分類
Windows | Unix | |
---|---|---|
RUNTIME | .exe、.dll | 可執行檔 |
LIBRARY | .so | |
ARCHIVE | .a、.lib 包含 dll 的連結介面 |
.a |
對應三者輸出位置的全域變數為 CMAKE_RUNTIME_OUTPUT_DIRECTORY、CMAKE_LIBRARY_OUTPUT_DIRECTORY 和 CMAKE_ARCHIVE_OUTPUT_DIRECTORY。通常我們可能會這樣指定:
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
上面這種寫法會將整個 binary tree 下所建置的執行檔以及靜態、共享程式庫搜集到建置目錄的 bin 和 lib 下。
同樣的,上面這些變數都有對應的 target 屬性,我們也可以直接指定屬性蓋過全域設定。相對應的屬性有
- RUNTIME_OUTPUT_DIRECTORY
- LIBRARY_OUTPUT_DIRECTORY
- ARCHIVE_OUTPUT_DIRECTORY
這些屬性都可以利用前面介紹過的 set_target_properties() 指令設定,作用同上述的全域變數,這裏就不在詳述。
安裝
編輯CMake 的安裝可以分為兩種,第一種是編譯完成後透過 make install 安裝,第二種是將建置好的檔案打包給用戶安裝,這裏只介紹第一種。
安裝 Target
編輯指令 install() 用來描述安裝路徑以及一些瑣碎的設定,每個 Target 可以安裝的檔案項目有五類:
- ARCHIVE
- LIBRARY
- RUNTIME
- FRAMEWORK
- BUNDLE
其中 ARCHIVE、LIBRARY、RUNTIME 如上所述,FRAMEWORK 和 BUNDLE 屬於 Mac OS X 專有,這裏不另介紹。另外 install() 指令也可以安裝下列 target 屬性所指定的檔案:
- PRIVATE_HEADER
- PUBLIC_HEADER
- RESOURCE
每一個可安裝的檔案項目都可以分別設定 DESTINATION、PERMISSIONS、CONFIGURATIONS、COMPONENT 等選項,完整的格式看起來非常繁瑣:
install(TARGETS targets... [EXPORT <export-name>] [[ARCHIVE|LIBRARY|RUNTIME|FRAMEWORK|BUNDLE| PRIVATE_HEADER|PUBLIC_HEADER|RESOURCE] [DESTINATION <dir>] [PERMISSIONS permissions...] [CONFIGURATIONS [Debug|Release|...]] [COMPONENT <component>] [OPTIONAL] [NAMELINK_ONLY|NAMELINK_SKIP] ] [...])
實際撰寫看起來像這樣:
install(TARGETS targets... ARCHIVE DESTINATION <dir> PERMISSIONS permissions... CONFIGURATIONS [Debug|Release|...] COMPONENT <component> RUNTIME DESTINATION <dir> PERMISSIONS permissions... CONFIGURATIONS [Debug|Release|...] COMPONENT <component> PUBLIC_HEADER DESTINATION <dir> PERMISSIONS permissions... CONFIGURATIONS [Debug|Release|...] COMPONENT <component> )
最常用的參數是 DESTINATION,用以指定安裝的目的地。假如我們有 app、calc-s、calc 三個 target,install() 指令通常寫起來像這樣:
install(TARGETS app calc calc-s RUNTIME DESTINATION bin LIBRARY DESTINATION lib ARCHIVE DESTINATION lib/static PUBLIC_HEADER DESTINATION include )
有了install 指令 CMake 會幫我們在 makefile 當中產生 install 的 rule,當我們執行完 make install 之後,這些 target 所生成的執行檔或 dll會跑到安裝路徑下的 bin,靜態程式庫會跑到 lib/static,而so會跑到 lib 資料夾下。如果有指定 target 的 PUBLIC_HEADER 屬性,指定的 header 也會自己安裝到 include 資料夾下。
可是到底安裝到哪裏去了呢?安裝路徑由全域變數 CMAKE_INSTALL_PREFIX 控制。在 Windows 上預設是
- C:\Program Files\
在 Unix-like 系統預設是
- /usr/local/
我們可以在 CMakeLists 當中直接用 set() 設定 CMAKE_INSTALL_PREFIX 值,不過留到執行 cmake 再由命令列指定會比較有彈性。
cmake -DCMAKE_INSTALL_PREFIX="d:/my_precious" .
除了 CMAKE_INSTALL_PREFIX 之外,CMake 所產生出來的 Makefile 也支援 DESTDIR。有下面兩種使用方式:
$ make install DESTDIR="/some/absolute/path" 或 $ export DESTDIR="/some/absolute/path" $ make install
最後安裝的位置應該是
- DESTDIR/
- CMAKE_INSTALL_PREFIX/
- install 指定的 DESTINATION
- CMAKE_INSTALL_PREFIX/
除了 DESTINATION 外,另一個常用的選項是 PERMISSIONS,用以指定檔案存取權限,可用的選項有 OWNER_READ、OWNER_WRITE、OWNER_EXECUTE、GROUP_READ、GROUP_WRITE、 GROUP_EXECUTE、WORLD_READ、WORLD_WRITE、WORLD_EXECUTE、SETUID、SETGID。在不支援這些權限設定的平台上會自動忽略。
安裝其他文件
編輯相較於 target 安裝,其他文件相對簡單了一點點。
install(FILES files... DESTINATION <dir> [PERMISSIONS permissions...] [CONFIGURATIONS [Debug|Release|...]] [COMPONENT <component>] [RENAME <name>] [OPTIONAL] )
來源檔案是以目前工作目錄相對的路徑來表示。
另外還可以安裝整個目錄
install(DIRECTORY dirs... DESTINATION <dir> [FILE_PERMISSIONS permissions...] [DIRECTORY_PERMISSIONS permissions...] [USE_SOURCE_PERMISSIONS] [OPTIONAL] [CONFIGURATIONS [Debug|Release|...]] [COMPONENT <component>] [FILES_MATCHING] [[PATTERN <pattern> | REGEX <regex>] [EXCLUDE] [PERMISSIONS permissions...]] [...] )
這個功能會將來源目錄下的內容以樹狀方式複製至 DESTINATION 指定的目錄,請注意下面兩者來源目錄最後斜線的差別:
# 会将目录复制成为 dst/src/{subdirs and files...} install(DIRECTORY myproj/src DESTINATION dst) # 会将目录复制成为 dst/{subdirs and files...} install(DIRECTORY myproj/src/ DESTINATION dst)
對 Linux 使用者而言這樣的行為應該很熟悉。
參數 FILES_MATCHING 用於指定操作檔案的條件,可以使用 PATTERN 或 REGEX 兩種比對方式,要注意 PATTERN 會比對全路徑而不只是檔名。
install(DIRECTORY src/ DESTINATION include FILES_MATCHING PATTERN "*.h")
以上會把 src/ 底下所有副檔名為 .h 的檔案複製到 include 資料夾下,並且會保留原本目錄樹的架構。
在比對條件後面接 EXCLUDE 可用來排除符合條件的檔案或目錄,使其被 install 忽略。
install(DIRECTORY myapp/ mylib DESTINATION myproj PATTERN ".git" EXCLUDE )
- myapp 的內容會被複製到 myproj 下
- mylib 的內容會被複製到 myproj/mylib 下
- 所有完整路徑中包含「.git」的檔案或目錄都不會被複製,例如 myapp/.git/branches 等等。
範例
編輯結合前面所述,這裏以一個具體而微的範例當成結尾。
檔案樹
編輯- lib2/
- src/
- doc/
- doc.txt
- CMakeLists.txt
- calc.c
- calc.h
- readme.txt
- doc/
- src/
檔案內容
CMakeLists.txt
cmake_minimum_required(VERSION 2.6)
project(calc)
include_directories(${CMAKE_SOURCE_DIR})
# build
add_library(calc SHARED calc.c)
set_target_properties(calc
PROPERTIES
PUBLIC_HEADER "calc.h"
)
add_library(calc-static STATIC calc.c)
set_target_properties(calc-static
PROPERTIES
OUTPUT_NAME "calc"
PREFIX "lib"
PUBLIC_HEADER "calc.h"
)
# install
install(TARGETS calc calc-static
RUNTIME DESTINATION calc/bin
LIBRARY DESTINATION calc/lib
ARCHIVE DESTINATION calc/lib
PUBLIC_HEADER DESTINATION calc/include
)
install(DIRECTORY ../src/ DESTINATION calc
FILES_MATCHING PATTERN "*.txt"
PATTERN "CMakeLists.txt" EXCLUDE
)
calc.h
#ifndef CALC_H_
#define CALC_H_
#if defined(_WIN32) && defined(calc_EXPORTS)
# if defined(LIBCALC_INTERNAL)
# define LIBCALC_API __declspec (dllexport)
# else
# define LIBCALC_API __declspec (dllimport)
# endif
#endif
#if !defined(LIBCALC_API)
# define LIBCALC_API
#endif
LIBCALC_API int Cube(int x);
LIBCALC_API int Square(int x);
#endif
calc.c
#define LIBCALC_INTERNAL
#include <calc.h>
LIBCALC_API int Cube(int x)
{
return x * x * x;
}
LIBCALC_API int Square(int x)
{
return x * x;
}
剩下 readme.txt 和 doc/doc.txt 的內容就交給讀者自行發揮。
建置
編輯假設現在的工作目錄在lib2/,執行以下命令:
$ mkdir build $ cd build $ cmake -DCMAKE_INSTALL_PREFIX=讀者自行指定 ../src $ make install
在 Linux 上的安裝結果:
- ${CMAKE_INSTALL_PREFIX}/
- calc/
- bin/
- doc/
- doc.txt
- include/
- calc.h
- lib/
- libcalc.so
- libcalc.a
- readme.txt
- calc/
在 Windows 上得到的安裝結果:
- ${CMAKE_INSTALL_PREFIX}/
- calc/
- bin/
- libcalc.dll
- doc/
- doc.txt
- include/
- calc.h
- lib/
- libcalc.dll.a
- libcalc.a
- readme.txt
- bin/
- calc/