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/