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

除了 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

檔案內容

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


在 Windows 上得到的安裝結果:

  • ${CMAKE_INSTALL_PREFIX}/
    • calc/
      • bin/
        • libcalc.dll
      • doc/
        • doc.txt
      • include/
        • calc.h
      • lib/
        • libcalc.dll.a
        • libcalc.a
      • readme.txt