上一节,我们学习了cmake工程的内部构建和外部构建的简单介绍。对于一个完整的工程来说,应该还需要有一些版本文件COPYRIGHT和说明文档README等,以及sh脚本。
本节,我们使用外部构建, 完善HelloWorld工程。任务如下:
- 为工程添加一个
子目录src,用来放置工程源代码。 - 添加一个
子目录doc,用来放置这个工程的文档hello.txt。 - 在工程目录添加
文本文件COPYRIGHT, README。 - 在工程目录添加一个
runhello.sh 脚本,用来调用目标文件hello。 - 将构建后的目标文件放入
构建目录的bin子目录。 - 最终安装这些文件:将hello 二进制目标文件与runhello.sh 安装至
/usr/bin,将doc目录的内容以及COPYRIGHT/README安装到/usr/share/doc/cmake/t2。
1. 准备工作
首先,递归构建 /backup/cmake 目录,用来放置我们的cmake工程。
mkdir -p /backup/cmake
然后在cmake 目录建立第二个练习目录t2。
cd /backup/cmake
mkdir t2
cd t2
在t2目录建立src子目录,在该目录下建立main.cpp和CMakeLists.txt:
mkdir src
cd src
touch main.cpp
main.cpp文件内容:
#include <iostream>
using namespace std;
int main(){
cout(“Hello World from t1 Main!\n”);
return 0;
}
CMakeLists.txt文件内容:
ADD_EXECUTABLE(hello main.c)
然后返回t2工程目录,建立CMakeLists.txt:
cd ..
touch CMakeLists.txt
vi CMakeLists.txt
CMakeLists.txt文件内容:
PROJECT(HELLO)
ADD_SUBDIRECTORY(src bin)
2. 构建工程
我们使用外部构建,在工程目录t2下建立build目录,进入build目录构建工程:
mkdir build
cd build
cmake ..
make
构建完成后,我们会发现生成的目标文件hello位于build/bin目录中。
3. 指令解释
- ADD_SUBDIRECTORY 指令
语法:
ADD_SUBDIRECTORY(source_dir [binary_dir]
该指令用于向当前工程添加存放源文件的子目录,并可以指定中间二进制和目标二进制存放的位置。
上面的ADD_SUBDIRECTORY(src bin) 的意思就是向当前工程t2添加源文件子目录src,然后指定了中间二进制文件和目标二进制文件的存放位置为build/bin。
4. 重新指定目标二进制文件的保存位置
不管ADD_SUBDIRECTORY 指令是否指定编译输出目录,我们都可以通过SET指令重新定义EXECUTABLE_OUTPUT_PATH 和LIBRARY_OUTPUT_PATH 变量 来指定最终的目标二进制的位置(指最终生成的hello 或者最终的共享库,不包含编译生成的中间文件):
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
在第一节我们提到了_BINARY_DIR 和PROJECT_BINARY_DIR 变量,他们指的编译发生的当前目录,因为我们这里是外部构建,所以上面两条指令分别定义了:
- 可执行二进制的输出路径为build/bin
- 库的输出路径为build/lib
注意,这两条指令是添加在src/CMakeLists.txt文件中,我们记住一个规律,在哪里ADD_EXECUTABLE 或ADD_LIBRARY,就在哪里加入上述的定义。
5. 安装文件
安装的方法有两种,一种是从代码编译后直接make install 安装,一种是打包时的指定目录安装。下面我们介绍第一种方法,来安装我们的HelloWorld。
我们需要用到一条新的cmake指令INSTALL 和 一个新的变量 CMAKE_INSTALL_PREFIX。
5.1 INSTALL 指令
该指令用于定义安装规则,安装的内容可以包括目标二进制、动态库、静态库以及文件、目录、脚本等。
- 目标文件的安装
语法:
INSTALL(TARGETS targets...[[ARCHIVE|LIBRARY|RUNTIME][DESTINATION <dir>][PERMISSIONS permissions...][CONFIGURATIONS
[Debug|Release|...]][COMPONENT <component>][OPTIONAL]]
targets...参数就是我们通过ADD_EXECUTABLE 或者ADD_LIBRARY定义的目标文件,可能是可执行二进制、动态库、静态库。
三种目标文件对应的文件类型有三种,ARCHIVE特指静态库,LIBRARY 特指动态库,RUNTIME 特指可执行目标二进制。
DESTINATION 定义了安装的路径,如果路径以/开头,那么指的是绝对路径,这时候CMAKE_INSTALL_PREFIX 其实就无效了。如果你希望使用CMAKE_INSTALL_PREFIX 来定义安装路径,就要写成相对路径,即不要以/开头,那么安装后的路径就是${CMAKE_INSTALL_PREFIX}/<DESTINATION 定义的路径>。
例子:
INSTALL(TARGETS myrun mylib mystaticlibRUNTIME DESTINATION binLIBRARY DESTINATION libARCHIVE DESTINATION libstatic)
上面的例子会将:可执行二进制myrun安装到${CMAKE_INSTALL_PREFIX}/bin,目录动态库libmylib安装到${CMAKE_INSTALL_PREFIX}/lib ,目录静态库libmystaticlib安装到${CMAKE_INSTALL_PREFIX}/libstatic 目录。
- 普通文件的安装
INSTALL(FILES files... DESTINATION <dir>[PERMISSIONS permissions...][CONFIGURATIONS [Debug|Release|...]][COMPONENT <component>][RENAME <name>] [OPTIONAL])
可用于安装一般文件,并可以指定访问权限。files是 该指令所在CMakeLists.txt文件 的所在路径 的文件。如果默认不定义权限PERMISSIONS,安装后的权限为:
OWNER_WRITE, OWNER_READ, GROUP_READ, 和WORLD_READ,即644 权限。
- 非目标文件的可执行程序安装(比如sh脚本之类):
语法:
INSTALL(PROGRAMS files... DESTINATION <dir>[PERMISSIONS permissions...][CONFIGURATIONS [Debug|Release|...]][COMPONENT <component>][RENAME <name>] [OPTIONAL])
跟上面的FILES 指令使用方法一样,唯一的不同是安装后权限为:
OWNER_EXECUTE, GROUP_EXECUTE, 和WORLD_EXECUTE,即755 权限
- 目录的安装
语法:
INSTALL(DIRECTORY dirs... DESTINATION <dir>[FILE_PERMISSIONS permissions...][DIRECTORY_PERMISSIONS permissions...][USE_SOURCE_PERMISSIONS][CONFIGURATIONS [Debug|Release|...]][COMPONENT <component>][[PATTERN <pattern> | REGEX <regex>]
[EXCLUDE] [PERMISSIONS permissions...]] [...])
dirs是安装的目录名,注意,abc 和 abc/ 有很大区别,如果目录名不以/结尾,那么这个目录将被安装为目标路径下的abc,如果目录名以/结尾,代表将这个目录中的内容安装到目标路径,但不包括这个目录本身。
例子:
INSTALL(DIRECTORY icons scripts/ DESTINATION share/myproj)
这条指令的结果是:将icons目录安装到${CMAKE_INSTALL_PREFIX}/share/myproj,将scripts目录的内容安装到${CMAKE_INSTALL_PREFIX}/share/myproj,但不包含scripts目录。
5.2 CMAKE_INSTALL_PREFIX 变量
这个变量是用来指明安装的总路径的,和cmake一起使用。下面我们举三个例子:
- 默认安装:安装到
/usr/local目录
cmake ..
make
make install
- 安装到系统:安装到系统的
/usr目录
cmake -DCMAKE_INSTALL_PREFIX=/usr ..
make
make install
- 安装到其他路径:安装到我们指定的
/tmp/t2/usr目录
cmake -DCMAKE_INSTALL_PREFIX=/tmp/t2/usr ..
make
make install
5.3 修改Helloworld支持安装
首先,我们先补上未添加的文件。
在工程目录中添加doc目录及文件:
cd /backup/cmake/t2
mkdir doc
vi doc/hello.txt
在工程目录中添加runhello.sh脚本,内容为:hello。
touch runhello.sh
vi runhello.sh
在工程目录中添加 COPYRIGHT 和 README:
touch COPYRIGHT
touch README
下面改写各目录的CMakeLists.txt 文件。
- 安装COPYRIGHT 和 README
直接修改工程目录的CMakeLists.txt,加入以下指令:
INSTALL(FILES COPYRIGHT README DESTINATION share/doc/cmake/t2)
- 安装runhello.sh
直接修改工程目录的CMakeLists.txt,加入以下指令:
INSTALL(PROGRAMS runhello.sh DESTINATION bin)
- 安装doc中的hello.txt
直接修改工程目录的CMakeLists.txt,加入以下指令:
INSTALL(DIRECTORY doc/ DESTINATION share/doc/cmake/t2)
当然还有更加简单的方法,就是通过在doc 目录建立 CMakeLists.txt 并将doc目录通过ADD_SUBDIRECTORY 加入工程来完成。
最后,我们进入build目录进行外部编译,通过使用CMAKE_INSTALL_PREFIX 参数,我们将它安装到/tmp/t2 目录:
cmake -DCMAKE_INSTALL_PREFIX=/tmp/t2/usr ..
make
make install
让我们进入/tmp/t2 目录看一下安装结果:
./usr
./usr/share
./usr/share/doc
./usr/share/doc/cmake
./usr/share/doc/cmake/t2
./usr/share/doc/cmake/t2/hello.txt
./usr/share/doc/cmake/t2/README
./usr/share/doc/cmake/t2/COPYRIGHT
./usr/bin./usr/bin/hello
./usr/bin/runhello.sh