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