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