目录
- 1. 参考文档
- 2. cmake优点
- 3. 什么时候用cmake
- 4. 第一部分:CMakeLists.txt语法
- 4.1. cmake基本使用方法
- 4.1.1. 定义和使用变量
- 4.1.1.1. 显示的定义变量
- 4.1.1.2. 变量的作用域
- 4.1.1.3. 获取变量值
- 4.1.1.4. 隐式变量
- 4.1.1.5. 常用变量
- 4.1.2. 打印信息语句:向终端输出用户自定义信息
- 4.1.3. 自定义触发条件(依赖关系)和执行Linux系统命令
- 4.1.3.1. 第一种形式是增加一个客制命令用来产生一个输出
- 4.1.3.2. 第二种形式:自定义增加一个新的目标,并定义依赖关系
- 4.1.3.3. 实例
- 4.1.4. CMakeLists.txt文件获取外部设置的变量的方法
- 4.1.4.1. 方法一: 使用环境变量(非常灵活,推荐使用,别人也可以在任意一个shell脚本中设置)
- 4.1.4.2. 方法二: 执行cmake ..时传入用户自定义变量(优点是可以扩展cmake官方定义的变量)
- 4.1.4.3. 方法三: 执行cmake ..时传入cmake官方定义的变量(优点是内部不用在赋值)
- 4.1.4.4. 注意: 这些变量会记录在 CMakeCache.txt中,如果不使用这些变量了其值依然存在,因此需要删除这些这个文件
- 4.1.4.5. 注意: 别人那里的xx.sh设置的环境变量在外部不生效
- 4.1.5. 其他功能
- 4.1.5.1. 获取生成的动态库存储路径
- 4.1.5.2. 注释符号
- 4.1.5.3. 控制编译选项
- 4.1.5.4. 定义 target 依赖的其他 target,确保在编译本 target 之前,其他的 target 已经被构建
- 4.1.5.5. 在 CMakeLists.txt 处理过程中执行命令,并不会在生成的 Makefile 中执行
- 4.1.5.6. FIND_指令
- 4.1.5.7. 控制指令
- 4.1.1. 定义和使用变量
- 4.2. 项目初始化设置
- 4.2.1. 下面这些变量,都可以通过在 CMakeLists.txt外部设置,在 CMakeLists.txt中使用
- 4.2.2. 显示的定义工程名
- 4.2.2.1. projectname表示工程名
- 4.2.2.2. [CXX] [C] [Java]支持的语言列表是可以忽略的, 默认情况表示支持所有语言
- 4.2.2.3. 这个指令隐式的定义了四个 cmake 变量:
- 4.2.3. 设置cmake最低版本
- 4.2.4. 显示更多的编译信息(方便调试)
- 4.2.5. 设置编译类型
- 4.2.6. 设置编译参数
- 4.2.6.1. 设置是否可gdb调试。三种方法
- 4.2.6.2. 设置是否条件编译
- 4.2.7. 安装路径
- 4.3. 编译工程设置(可执行文件,库等)
- 4.3.1. 第一步:设置头文件和库所在搜索路径
- 4.3.1.1. 设置头文件路径(可选)
- 4.3.1.2. 设置动态库so所在路径(可选,如果使用库的话)
- 4.3.1.3. 使用环境变量,添加头文件和动态库的搜索路径(可选)
- 4.3.2. 第二步:生成工程编译后生成的可执行文件(源码位于src文件夹下)
- 4.3.2.1. 设置定义可执行文件名称和编译列表
- 4.3.2.2. 设置可执行文件(目标二进制)或者库的位置
- 4.3.2.3. 将生成bin文件与动态库链接
- 4.3.3. 第二步:生成共享库
- 4.3.3.1. 设置定义动态库或者静态库名称和编译列表
- 4.3.3.2. 设置生成的动态库或者静态库的位置
- 4.3.3.3. 同时生成同名的动态库和静态库
- 4.3.1. 第一步:设置头文件和库所在搜索路径
- 4.4. 配置安装文件存储路径
- 4.4.1. 目标文件的安装(目标二进制、动态库、静态库)
- 4.4.1.1. install语句位置
- 4.4.1.2. 设置目标文件名
- 4.4.1.3. 设置目标类型
- 4.4.1.4. 设置安装路径
- 4.4.1.5. 例子1
- 4.4.1.6. 例子2
- 4.4.2. 普通文件的安装
- 4.4.2.1. install语句位置
- 4.4.2.2. 默认权限(不指定PERMISSIONS)
- 4.4.2.3. 例子
- 4.4.3. 非目标文件的可执行程序安装(比如脚本之类):
- 4.4.3.1. 跟上面的 FILES 指令使用方法一样,唯一的不同是安装后权限为
- 4.4.3.2. 例子
- 4.4.4. 目录的安装:
- 4.4.4.1. 所在 Source 目录的相对路径
- 4.4.4.2. PATTERN 用于使用正则表达式进行过滤
- 4.4.4.3. PERMISSIONS 用于指定 PATTERN 过滤后的文件权限
- 4.4.4.4. 例子1
- 4.4.4.5. 例子2
- 4.4.4.6. 安装文件夹到目的位置,并将文件夹改名
- 4.4.5. 安装时 CMAKE 脚本的执行:
- 4.4.6. 安装结果make install
- 4.4.1. 目标文件的安装(目标二进制、动态库、静态库)
- 4.1. cmake基本使用方法
- 5. 第二部分:工程文件夹组织
- 5.1. 工程的 CMakeLists.txt文件
- 5.2. src文件夹
- 5.3. doc文件夹
- 5.4. 文本文件COPYRIGHT, README
- 5.5. runhello.sh 脚本
- 5.6. build构建文件夹
- 5.7. 最终安装这些文件:将 hello 二进制与 runhello.sh 安装至/usr/bin,将 doc 目录 的内容以及 COPYRIGHT/README 安装到/usr/share/doc/cmake/t2
- 6. 第三部分:使用cmake构建程序
- 6.1. 开始构建
- 6.1.1. 外部构建(推荐使用)
- 6.1.1.1. 优点
- 6.1.1.2. 开始构建
- 6.1.1.3. 外部编译的过程
- 6.1.2. 内部构建
- 6.1.2.1. 缺点
- 6.1.2.2. 开始构建
- 6.1.1. 外部构建(推荐使用)
- 6.2. 安装文件(拷贝)
- 6.2.1. 方法一:代码编译后直接make install安装
- 6.2.2. 方法二:打包时的指定目录安装。
- 6.2.3. 一个非常有用的变量
- 6.2.4. 例子
- 6.2.5. 其他方案
- 6.2.5.1. Makefile
- 6.2.5.2. autotools 工程
- 6.3. 清理构建结果
- 6.4. 运行程序
- 6.1. 开始构建
1 参考文档
CMakePractice.pdf
MyCompilation/CMakeCompilation.md at master · wangruchen/MyCompilation
2 cmake优点
- 跨平台,并可生成 native 编译配置文件,在 Linux/Unix 平台,生成 makefile,在 苹果平台,可以生成 xcode,在 Windows 平台,可以生成 MSVC 的工程文件。
- 能够管理大型项目,KDE4 就是最好的证明。
3 什么时候用cmake
- 如果你没有实际的项目需求,那么看到这里就可以停下来了,因为 cmake 的学习过程就是实践过程,没有实践,读的再多几天后也会忘记。
- 如果你的工程只有几个文件,直接编写 Makefile 是最好的选择。
- 如果使用的是 C/C++/Java 之外的语言,请不要使用 cmake(至少目前是这样)
- 如果你使用的语言有非常完备的构建体系,比如 java 的 ant,也不需要学习 cmake,虽然有成功的例子,比如 QT4.3 的 csharp 绑定 qyoto。
- 如果项目已经采用了非常完备的工程管理工具,并且不存在维护问题,没有必要迁移到cmake
- 如果仅仅使用 qt 编程,没有必要使用 cmake,因为 qmake 管理 Qt 工程的专业性和自 动化程度比 cmake 要高很多。
4 第一部分:CMakeLists.txt语法
4.1 cmake基本使用方法
4.1.1 定义和使用变量
4.1.1.1 显示的定义变量
SET(VAR [VALUE] [CACHE TYPE DOCSTRING [FORCE]])
SET(SRC_LIST main.c t1.c t2.c)
4.1.1.2 变量的作用域
- 方法一 父子目录天然可以共享变量,子目录可以访问父目录定义的变量
- 方法二 set 任意目录之间互相访问同一变量
将变量存到CMakeCache.txt
set(variable value CACHE INTERNAL docstring ) #variable = value , docstring不能提示字符,不能省略
- 方法三 set_property/get_property
- 在opencl下的CMakeLists.txt中定义一个名为INCLUDE_OPENCL_1_2 的global property
set_property(GLOBAL PROPERTY INCLUDE_OPENCL_1_2 "${CMAKE_CURRENT_LIST_DIR}/include/1.2" )
- 在facedetect下的CMakeLists.txt中读取这个一个property
//先调用get_property将这个property读取到一个变量中(variable)INCLUDE_OPENCL
get_property(INCLUDE_OPENCL GLOBAL PROPERTY "INCLUDE_OPENCL_1_2" )
//显示INCLUDE_OPENCL
MESSAGE(STATUS "INCLUDE_OPENCL :${INCLUDE_OPENCL}")
- 在opencl下的CMakeLists.txt中定义一个名为INCLUDE_OPENCL_1_2 的global property
4.1.1.3 获取变量值
- 变量使用${}方式取值,但是在 IF 控制语句中是直接使用变量名
- 指令(参数1 参数2…)
指令是大小写无关的,参数和变量是大小写相关的。但,推荐你全部使用大写指令。
参数使用括弧括起,参数之间使用空格或分号分开。
ADD_EXECUTABLE(hello main.c func.c)或者ADD_EXECUTABLE(hello main.c;func.c)
4.1.1.4 隐式变量
PROJECT 指令,他 会隐式的定义<projectname>_BINARY_DIR 和<projectname>_SOURCE_DIR 两个变量。
4.1.1.5 常用变量
- 当前处理的 CMakeLists.txt 所在的路径,比如上面我们提到的 src 子目录。
CMAKE_CURRENT_SOURCE_DIR
CMAKE_CURRRENT_BINARY_DIR
如果是 in-source 编译,它跟 CMAKE_CURRENT_SOURCE_DIR 一致,如果是 out-of- source 编译,他指的是 target 编译目录。
使用我们上面提到的ADD_SUBDIRECTORY(src bin)可以更改这个变量的值。
使用SET(EXECUTABLE_OUTPUT_PATH <新路径>)并不会对这个变量造成影响,它仅仅 修改了最终目标文件存放的路径。
- 输出调用这个变量的 CMakeLists.txt 的完整路径
CMAKE_CURRENT_LIST_FILE
- 输出这个变量所在的行
CMAKE_CURRENT_LIST_LINE
- 定义自己的 cmake 模块所在的路径
CMAKE_MODULE_PATH
如果你的工程比较复杂,有可能会自己 编写一些 cmake 模块,这些 cmake 模块是随你的工程发布的,为了让 cmake 在处理 CMakeLists.txt 时找到这些模块,你需要通过 SET 指令,将自己的 cmake 模块路径设 置一下。
比如
SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) 这时候你就可以通过 INCLUDE 指令来调用自己的模块了。
- 重新定义最终结果的存放目录
EXECUTABLE_OUTPUT_PATH 和 LIBRARY_OUTPUT_PATH
- 返回通过 PROJECT 指令定义的项目名称
PROJECT_NAME
4.1.2 打印信息语句:向终端输出用户自定义信息
MESSAGE([SEND_ERROR | STATUS | FATAL_ERROR] "message to display"…)
MESSAGE(STATUS "This is BINARY dir " ${HELLO_BINARY_DIR})
- SEND_ERROR,产生错误,生成过程被跳过。
- SATUS,输出前缀为—的信息。
- FATAL_ERROR,立即终止所有 cmake 过程
4.1.3 自定义触发条件(依赖关系)和执行Linux系统命令
4.1.3.1 第一种形式是增加一个客制命令用来产生一个输出
类似makefile的依赖关系(在makefile中产生一个新目标)
add_custom_command(OUTPUT output1 [output2 ...] COMMAND command1[ARGS] [args1...] [COMMAND command2 [ARGS] [args2...] ...] [MAIN_DEPENDENCYdepend] [DEPENDS[depends...]] [IMPLICIT_DEPENDS<lang1> depend1 ...] [WORKING_DIRECTORYdir] [COMMENT comment] [VERBATIM] [APPEND])
结果:
在Makefile中,它会产生一个这样的新目标: OUTPUT: MAIN_DEPENDENCY DEPENDS COMMAND
4.1.3.2 第二种形式:自定义增加一个新的目标,并定义依赖关系
- 第一步:新增一个目标,并定义依赖关系
add_custom_target(Name [ALL] [command1 [args1...]] [COMMAND command2 [args2...] ...] [DEPENDS depend depend depend ... ]//依赖的东西新了才会触发执行该目标 [WORKING_DIRECTORY dir] [COMMENT comment] [VERBATIM] [SOURCES src1 [src2...]])
ALL 表示每次都会执行,不需要依赖
- 第二步:定义新增的目标在什么阶段触发什么命令
add_custom_command(TARGET target PRE_BUILD | PRE_LINK| POST_BUILD COMMAND command1[ARGS] [args1...] [COMMAND command2[ARGS] [args2...] ...] [WORKING_DIRECTORYdir] [COMMENT comment][VERBATIM])
4.1.3.3 实例
- 定义依赖关系
add_custom_target(Test1 ALL DEPENDS ${TEST_FILE}) add_custom_command(TARGET Test1 PRE_BUILD COMMAND echo "executing a fake command" COMMENT "This command will be executed before building target Test1" )
c++ - cmake: include library dependencies in static lib - Stack Overflow
- 执行系统命令,复制文件
- 方法一: build前、后执行系统命令(推荐使用)
ADD_EXECUTABLE(main ${SRC_LIST})
add_custom_command(TARGET main
POST_BUILD # 构建之后(执行make生成main之后,复制main到目的地
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:main> ~/result #$<TARGET_FILE:main>为main所在的路径
)
- 方法二: configuration阶段执行系统命令
makefile - Running a bash command via CMake - Stack Overflow
ADD_EXECUTABLE(main ${SRC_LIST})
execute_process(
COMMAND ${CMAKE_COMMAND} -E copy main ~/result
)
报错,找不到main,原因是配置阶段(执行cmake ..后,尚未make)尚未生成main。
- 方法一: build前、后执行系统命令(推荐使用)
4.1.4 CMakeLists.txt文件获取外部设置的变量的方法
4.1.4.1 方法一: 使用环境变量(非常灵活,推荐使用,别人也可以在任意一个shell脚本中设置)
- 任意一个脚本文件设置环境变量
export DEMUX_IS_DECODE="xxx"
- CMakeLists.txt中使用该环境变量
MESSAGE(STATUS "$ENV{DEMUX_IS_DECODE}")
4.1.4.2 方法二: 执行cmake ..时传入用户自定义变量(优点是可以扩展cmake官方定义的变量)
- 执行cmake时设置自定义变量
cmake .. -DMY_VAR=bbbb
- CMakeLists.txt中使用这个自定义变量
MESSAGE(STATUS "${MY_VAR}")
4.1.4.3 方法三: 执行cmake ..时传入cmake官方定义的变量(优点是内部不用在赋值)
- 执行cmake时,设置cmake定义的变量
cmake .. -DCMAKE_C_FLAGS="-DDECODE"
- CMakeLists.txt中使用这个变量
MESSAGE(STATUS "${MY_VAR}")
4.1.4.4 注意: 这些变量会记录在 CMakeCache.txt中,如果不使用这些变量了其值依然存在,因此需要删除这些这个文件
rm CMakeCache.txt
4.1.4.5 注意: 别人那里的xx.sh设置的环境变量在外部不生效
原因是: 执行xx.sh的方式不对, env|grep var找不到
正确方式: . xx.sh
4.1.5 其他功能
4.1.5.1 获取生成的动态库存储路径
How to use CMake generator expression $<TARGET_FILE:tgt>? - Stack Overflow
add_custom_target( GenexDemo ALL COMMAND ${CMAKE_COMMAND} -E echo "$<TARGET_FILE:tgt1>"//tgt2可以为库名 VERBATIM )
4.1.5.2 注释符号
4.1.5.3 控制编译选项
- CMAKE_C_FLAGS
设置 C 编译选项,也可以通过指令 ADD_DEFINITIONS()添加。
- CMAKE_CXX_FLAGS
设置 C++编译选项,也可以通过指令 ADD_DEFINITIONS()添加。
- ADD_DEFINITIONS
向 C/C++编译器添加-D 定义,比如:
ADD_DEFINITIONS(-DENABLE_DEBUG -DABC),参数之间用空格分割。
如果你的代码中定义了#ifdef ENABLE_DEBUG #endif,这个代码块就会生效。
如果要添加其他的编译器开关,可以通过 CMAKE_C_FLAGS 变量和 CMAKE_CXX_FLAGS 变量设置。
4.1.5.4 定义 target 依赖的其他 target,确保在编译本 target 之前,其他的 target 已经被构建
ADD_DEPENDENCIES(target-name depend-target1
depend-target2 …)
4.1.5.5 在 CMakeLists.txt 处理过程中执行命令,并不会在生成的 Makefile 中执行
EXEC_PROGRAM(Executable [directory in which to run]
[ARGS <arguments to executable>]
[OUTPUT_VARIABLE <var>]
[RETURN_VALUE <var>]) 用于在指定的目录运行某个程序,通过 ARGS 添加参数,如果要获取输出和返回值,可通过
OUTPUT_VARIABLE 和 RETURN_VALUE 分别定义两个变量.
这个指令可以帮助你在 CMakeLists.txt 处理过程中支持任何命令,比如根据系统情况去
修改代码文件等等。
举个简单的例子,我们要在 src 目录执行 ls 命令,并把结果和返回值存下来。
可以直接在 src/CMakeLists.txt 中添加:
EXEC_PROGRAM(ls ARGS "*.c" OUTPUT_VARIABLE LS_OUTPUT RETURN_VALUE
LS_RVALUE)
IF(not LS_RVALUE)
MESSAGE(STATUS "ls result: " ${LS_OUTPUT})
ENDIF(not LS_RVALUE)
在cmake 生成Makefile的过程中,就会执行ls命令,如果返回0,则说明成功执行, 那么就输出ls*.c的结果。关于IF语句,后面的控制指令会提到。
4.1.5.6 FIND_指令
FIND_FILE(<VAR> name1 path1 path2 …)
VAR 变量代表找到的文件全路径,包含文件名 FIND_LIBRARY(<VAR> name1 path1 path2 …)
VAR 变量表示找到的库全路径,包含库文件名 FIND_PATH(<VAR> name1 path1 path2 …)
VAR 变量代表包含这个文件的路径。 FIND_PROGRAM(<VAR> name1 path1 path2 …)
VAR 变量代表包含这个程序的全路径。
FIND_PACKAGE(<name> [major.minor] [QUIET] [NO_MODULE]
[[REQUIRED|COMPONENTS] [componets…]])
用来调用预定义在 CMAKE_MODULE_PATH 下的 Find<name>.cmake 模块,你也可以自己 定义Find<name>模块,通过SET(CMAKE_MODULE_PATH dir)将其放入工程的某个目录 中供工程使用,我们在后面的章节会详细介绍 FIND_PACKAGE 的使用方法和 Find 模块的 编写。
FIND_LIBRARY 示例:
FIND_LIBRARY(libX X11 /usr/lib) IF(NOT libX)
MESSAGE(FATAL_ERROR “libX not found”)
ENDIF(NOT libX)
4.1.5.7 控制指令
- if
IF(expression)
COMMAND1(ARGS …)
COMMAND2(ARGS …)
…
ELSE(expression)
COMMAND1(ARGS …)
COMMAND2(ARGS …)
…
ENDIF(expression)
另外一个指令是 ELSEIF,总体把握一个原则,凡是出现 IF 的地方一定要有对应的ENDIF.出现 ELSEIF 的地方,ENDIF 是可选的。
表达式的使用方法如下:
IF(var),如果变量不是:空,0,N, NO, OFF, FALSE, NOTFOUND 或 <var>_NOTFOUND 时,表达式为真。
IF(NOT var ),与上述条件相反。
IF(var1 AND var2),当两个变量都为真是为真。
IF(var1 OR var2),当两个变量其中一个为真时为真。
IF(COMMAND cmd),当给定的cmd确实是命令并可以调用是为真。
IF(EXISTS dir)或者IF(EXISTS file),当目录名或者文件名存在时为真。
IF(file1 IS_NEWER_THAN file2),当 file1 比 file2 新,或者 file1/file2 其 中有一个不存在时为真,文件名请使用完整路径。
IF(IS_DIRECTORY dirname),当dirname是目录时,为真。
IF(variable MATCHES regex)
IF(string MATCHES regex) 当给定的变量或者字符串能够匹配正则表达式 regex 时为真。比如: IF("hello" MATCHES "ell")
MESSAGE("true")
ENDIF("hello" MATCHES "ell")
IF(variable LESS number)
IF(string LESS number)
IF(variable GREATER number)
IF(string GREATER number)
IF(variable EQUAL number)
IF(string EQUAL number)
数字比较表达式
IF(variable STRLESS string)
IF(string STRLESS string)
IF(variable STRGREATER string) IF(string STRGREATER string) IF(variable STREQUAL string)
IF(string STREQUAL string) 按照字母序的排列进行比较.
IF(DEFINED variable),如果变量被定义,为真。
一个小例子,用来判断平台差异:
IF(WIN32)
MESSAGE(STATUS “This is windows.”) #作一些 Windows 相关的操作
ELSE(WIN32)
MESSAGE(STATUS “This is not windows”) #作一些非 Windows 相关的操作
ENDIF(WIN32)
上述代码用来控制在不同的平台进行不同的控制,但是,阅读起来却并不是那么舒服, ELSE(WIN32)之类的语句很容易引起歧义。
这就用到了我们在“常用变量”一节提到的 CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS开 关。
可以SET(CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS ON) 这时候就可以写成:
IF(WIN32)
ELSE()
ENDIF()
如果配合 ELSEIF 使用,可能的写法是这样: IF(WIN32)
#do something related to WIN32 ELSEIF(UNIX)
#do something related to UNIX
ELSEIF(APPLE)
#do something related to APPLE
ENDIF(WIN32)
- while
WHILE 指令的语法是:
WHILE(condition)
COMMAND1(ARGS …)
COMMAND2(ARGS …)
…
ENDWHILE(condition) 其真假判断条件可以参考 IF 指令。
- FOREACH
FOREACH 指令的使用方法有三种形式:
- 列表
FOREACH(loop_var arg1 arg2 …)
COMMAND1(ARGS …)
COMMAND2(ARGS …)
…
ENDFOREACH(loop_var) 像我们前面使用的 AUX_SOURCE_DIRECTORY 的例子 AUX_SOURCE_DIRECTORY(. SRC_LIST) FOREACH(F SRCLIST)MESSAGE(
- 列表
-
- {F})
ENDFOREACH(F)
- 范围
FOREACH(loop_var RANGE total)
ENDFOREACH(loop_var)
从 0 到 total 以1为步进
举例如下:
FOREACH(VAR RANGE 10) MESSAGE(${VAR}) ENDFOREACH(VAR) 最终得到的输出是:
0
1
2
3
4
5
6
7
8
9
10
- 范围和步进
FOREACH(loop_var RANGE start stop [step])
ENDFOREACH(loop_var)
从 start 开始到 stop 结束,以 step 为步进,
举例如下
FOREACH(A RANGE 5 15 3)
MESSAGE(${A})
ENDFOREACH(A)
最终得到的结果是:
5
8
11
14
这个指令需要注意的是,知道遇到 ENDFOREACH 指令,整个语句块才会得到真正的执行。
- {F})
4.2 项目初始化设置
4.2.1 下面这些变量,都可以通过在 CMakeLists.txt外部设置,在 CMakeLists.txt中使用
4.2.2 显示的定义工程名
PROJECT(projectname [CXX] [C] [Java])
4.2.2.1 projectname表示工程名
4.2.2.2 [CXX] [C] [Java]支持的语言列表是可以忽略的, 默认情况表示支持所有语言
4.2.2.3 这个指令隐式的定义了四个 cmake 变量:
外部编译 | 内部编译 | 性质 | |
---|---|---|---|
<projectname>_SOURCE_DIR | cmake ..变量值为..所指的路径 | 执行cmake时所在路径 | 变量名随工程名变化 |
<projectname>_BINARY_DIR | 执行cmake时所在路径 | 执行cmake时所在路径 | |
PROJECT_SOURCE_DIR | 值为与上面两个变量相等 | 建议使用。变量名不随工程名变化。即使修改了工程名,也不影响这两个变量名 | |
PROJECT_BINARY_DIR |
使用PROJECT(HELLO)设置工程名为HELLO,这两个变量为HELLO_BINARY_DIR 和 HELLO_SOURCE_DIR。
使用PROJECT(xxx)设置工程名为xxx,这两个变量为xxx_BINARY_DIR 和 xxx_SOURCE_DIR。
- 内部编译
这里就是 HELLO_BINARY_DIR 和 HELLO_SOURCE_DIR(所以 CMakeLists.txt 中两个 MESSAGE 指令可以直接使用了这两个变量)。
因为采用的是内部编译,两个变量目前指的都是工程所在路径/backup/cmake/t1。
后面我们会讲到外部编译,两者所指代的内容会有所不同。
- 外部编译(与外部编译的值不一样)
HELLO_SOURCE_DIR 仍然指代工程路径,即/backup/cmake/t1
HELLO_BINARY_DIR 则指代编译路径,即/backup/cmake/t1/build
- 同时cmake 系统也帮助我们预定义了 PROJECT_BINARY_DIR 和 PROJECT_SOURCE_DIR 变量,他们的值分别跟 HELLO_BINARY_DIR 与 HELLO_SOURCE_DIR 一致。为了统一起见,建议以后直接使用 PROJECT_BINARY_DIR,PROJECT_SOURCE_DIR,即 使修改了工程名称,也不会影响这两个变量。如果使用了 <projectname>_SOURCE_DIR,修改工程名称后,需要同时修改这些变量。
4.2.3 设置cmake最低版本
cmake_minimum_required(VERSION 2.8) #指定cmake最低版本
4.2.4 显示更多的编译信息(方便调试)
set(CMAKE_VERBOSE_MAKEFILE ON)
4.2.5 设置编译类型
None : 编译器预设值
Debug : 产生除错资讯
Release : 进行执行速度最佳化
RelWithDebInfo : 进行执行速度最佳化,但仍然会启用 debug flag
MinSizeRel : 进行程式码最小化
- CMakeLists.txt添加 set CMAKE_BUILD_TYPE
-
执行时
假设我们现在工作目录是 ex2/build,我们可以执行以下的指令产生 Release 组态的执行档
$ mkdir release
$ cd release
$ cmake -DCMAKE_BUILD_TYPE=Release ../../src
4.2.6 设置编译参数
4.2.6.1 设置是否可gdb调试。三种方法
-
ADD_DEFINITIONS
ADD_DEFINITIONS(//定义编译参数 -g -ggdb3 -Wall -Wextra -pthread -Wno-unused-parameter -Woverloaded-virtual -Wpointer-arith -Wshadow -march=native -rdynamic -std=c++11)
- 设置相关变量
SET(CMAKE_CXX_FLAGS "-g") #指定编译器 参数
- 使用用户在 CMakeLists.txt外部设置的变量
4.2.6.2 设置是否条件编译
-
ADD_DEFINITIONS( -g -DWRITE_FRAME_INFO )
- set CMAKE_C_FLAGS
- 使用用户在 CMakeLists.txt文件外部设置的变量
4.2.7 安装路径
SET(CMAKE_INSTALL_PREFIX /usr/local/testmain) #设置安装目录
4.3 编译工程设置(可执行文件,库等)
4.3.1 第一步:设置头文件和库所在搜索路径
4.3.1.1 设置头文件路径(可选)
为了让我们的工程能够找到 hello.h 头文件,我们需要引入一个新的指令 INCLUDE_DIRECTORIES,其完整语法为:
INCLUDE_DIRECTORIES([AFTER|BEFORE] [SYSTEM] dir1 dir2 …)
这条指令可以用来向工程添加多个特定的头文件搜索路径,路径之间用空格分割,如果路径中包含了空格,可以使用双引号将它括起来,默认的行为是追加到当前的头文件搜索路径的后面,你可以通过两种方式来进行控制搜索路径添加的方式:
- CMAKE_INCLUDE_DIRECTORIES_BEFORE,通过 SET 这个 cmake 变量为 on,可以将添加的头文件搜索路径放在已有路径的前面。
- 通过 AFTER 或者 BEFORE 参数,也可以控制是追加还是置前。
现在我们在 src/CMakeLists.txt 中添加一个头文件搜索路径,方式很简单,加入:
INCLUDE_DIRECTORIES(/usr/include/hello)
进入 build 目录,重新进行构建,这是找不到 hello.h 的错误已经消失,但是出现了一个新的错误:
main.c:(.text+0x12): undefined reference to `HelloFunc' 因为我们并没有 link 到共享库 libhello 上。
4.3.1.2 设置动态库so所在路径(可选,如果使用库的话)
- 方法一:LINK_DIRECTORIES动态库文件夹搜索路径
LINK_DIRECTORIES(directory1 directory2 …)
添加非标准的共享库搜索路径,比如,在工程内部同时存在共享库和可执行二进制,在编译时就需要指定一下这些共享库的路径。
- 方法二:设置单个动态库搜索路径
LINK_LIBRARIES("/opt/MATLAB/R2012a/bin/glnxa64/libeng.so")
4.3.1.3 使用环境变量,添加头文件和动态库的搜索路径(可选)
CMAKE_INCLUDE_PATH 和 CMAKE_LIBRARY_PATH
这两个是环境变量而不是 cmake 变量。
使用方法是要在 bash 中用 export 或者在 csh 中使用 set 命令设置或者 CMAKE_INCLUDE_PATH=/home/include cmake ..等方式。
我们以本例中的 hello.h 为例,它存放在/usr/include/hello 目录,所以直接查找肯定是找不到的。
前面我们直接使用了绝对路径 INCLUDE_DIRECTORIES(/usr/include/hello)告诉工 程这个头文件目录。
为了将程序更智能一点,我们可以使用 CMAKE_INCLUDE_PATH 来进行,使用 bash 的方法 如下:
export CMAKE_INCLUDE_PATH=/usr/include/hello
然后在头文件中将 INCLUDE_DIRECTORIES(/usr/include/hello)替换为: FIND_PATH(myHeader hello.h)
IF(myHeader)
INCLUDE_DIRECTORIES(${myHeader})
ENDIF(myHeader)
这里简单说明一下,FIND_PATH 用来在指定路径中搜索文件名,比如:
FIND_PATH(myHeader NAMES hello.h PATHS /usr/include
/usr/include/hello)
这里我们没有指定路径,但是,cmake 仍然可以帮我们找到 hello.h 存放的路径,就是因 为我们设置了环境变量 CMAKE_INCLUDE_PATH。
如果你不使用 FIND_PATH,CMAKE_INCLUDE_PATH 变量的设置是没有作用的,你不能指 望它会直接为编译器命令添加参数-I<CMAKE_INCLUDE_PATH>。
以此为例,CMAKE_LIBRARY_PATH 可以用在 FIND_LIBRARY 中。
同样,因为这些变量直接为 FIND_指令所使用,所以所有使用 FIND_指令的 cmake 模块都 会受益。
4.3.2 第二步:生成工程编译后生成的可执行文件(源码位于src文件夹下)
4.3.2.1 设置定义可执行文件名称和编译列表
ADD_EXECUTABLE(hello ${SRC_LIST})
定义了这个工程会生成一个文件名为 hello 的可执行文件,相关的源文件是 SRC_LIST 中定义的源文件列表, 本例中你也可以直接写成ADD_EXECUTABLE(hello main.c)。
作为工程名的 HELLO 和生成的可执行文件 hello 是没有任何关系的。
4.3.2.2 设置可执行文件(目标二进制)或者库的位置
- 目标设置最终生成的 hello 或者最终的共享库,不包含编译生成的中间文件。
- 语法
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
PROJECT_BINARY_DIR他们指的编译发生的当前目录(build下执行cmake ..,那么是build下)。如果是内部编译,就相当于 PROJECT_SOURCE_DIR 也就是工程代码所在目录,如果是外部编译,指的是外部编译所在目录,也就是本例中的 build 目录。
- 语法位置
在哪里 ADD_EXECUTABLE 或 ADD_LIBRARY, 如果需要改变目标存放路径,就在哪里加入上述的定义。
4.3.2.3 将生成bin文件与动态库链接
- 错误的做法
- 准备工作:
请在/backup/cmake 目录建立 t4 目录,本节所有资源将存储在 t4 目录。
- 重复以前的步骤,建立 src 目录,编写源文件 main.c,内容如下: #include <hello.h>
int main()
{
HelloFunc();
return 0; }
编写工程主文件 CMakeLists.txt PROJECT(NEWHELLO) ADD_SUBDIRECTORY(src)
编写 src/CMakeLists.txt ADD_EXECUTABLE(main main.c)
上述工作已经严格按照我们前面季节提到的内容完成了。
- 外部构建
按照习惯,仍然建立 build 目录,使用 cmake ..方式构建。
过程:
cmake ..
make
构建失败,如果需要查看细节,可以使用第一节提到的方法
make VERBOSE=1来构建
错误输出为是:
/backup/cmake/t4/src/main.c:1:19: error: hello.h: 没有那个文件或目录
- 引入头文件搜索路径。
hello.h 位于/usr/include/hello 目录中,并没有位于系统标准的头文件路径,
(有人会说了,白痴啊,你就不会include <hello/hello.h>,同志,要这么干,我这 一节就没什么可写了.
- 准备工作:
- 将动态库链接到bin文件 TARGET_LINK_LIBRARIES
TARGET_LINK_LIBRARIES(target library1
<debug | optimized> library2
…)
这个指令可以用来为 target 添加需要链接的共享库,本例中是一个可执行文件,但是同样可以用于为自己编写的共享库添加共享库链接。
为了解决我们前面遇到的 HelloFunc 未定义错误,我们需要作的是向
src/CMakeLists.txt 中添加如下指令: TARGET_LINK_LIBRARIES(main hello)
也可以写成
TARGET_LINK_LIBRARIES(main libhello.so) 这里的 hello 指的是我们上一节构建的共享库 libhello.
TARGET_LINK_LIBRARIES(myProject libeng.so) #这些库名写法都可以。
TARGET_LINK_LIBRARIES(myProject eng)
TARGET_LINK_LIBRARIES(myProject -leng)
- 实例
-
实例1
cmake_minimum_required(VERSION 2.8 FATAL_ERROR) include_directories("/opt/MATLAB/R2012a/extern/include") #directly link to the libraries. LINK_LIBRARIES("/opt/MATLAB/R2012a/bin/glnxa64/libeng.so") LINK_LIBRARIES("/opt/MATLAB/R2012a/bin/glnxa64/libmx.so") #equals to below #LINK_LIBRARIES("/opt/MATLAB/R2012a/bin/glnxa64/libeng.so" "/opt/MATLAB/R2012a/bin/glnxa64/libmx.so") add_executable(myProject main.cpp)
cmake_minimum_required(VERSION 2.8 FATAL_ERROR) include_directories("/opt/MATLAB/R2012a/extern/include") LINK_DIRECTORIES("/opt/MATLAB/R2012a/bin/glnxa64") add_executable(myProject main.cpp) target_link_libraries(myProject eng mx) #equals to below #target_link_libraries(myProject -leng -lmx) #target_link_libraries(myProject libeng.so libmx.so)
- 实例2
CMake快速入门例子
-
4.3.3 第二步:生成共享库
4.3.3.1 设置定义动态库或者静态库名称和编译列表
ADD_LIBRARY(libname [SHARED|STATIC|MODULE]
[EXCLUDE_FROM_ALL]
source1 source2 … sourceN)
不需要写全 libhello.so,只需要填写 hello 即可,cmake 系统会自动为你生成 libhello.X
- 动态库类型
SHARED,动态库
STATIC,静态库
MODULE,在使用 dyld 的系统有效,如果不支持 dyld,则被当作 SHARED 对待。
- EXCLUDE_FROM_ALL 参数的意思是这个库不会被默认构建,除非有其他的组件依赖或者手工构建。
4.3.3.2 设置生成的动态库或者静态库的位置
- 目标设置最终生成的 hello 或者最终的共享库,不包含编译生成的中间文件。
- 语法
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
PROJECT_BINARY_DIR他们指的编译发生的当前目录,如果是内部编译,就相当于 PROJECT_SOURCE_DIR 也就是工程代码所在目录,如果是外部编译,指的是外部编译所在目录,也就是本例中的 build 目录。
- 语法位置
在哪里 ADD_EXECUTABLE 或 ADD_LIBRARY, 如果需要改变目标存放路径,就在哪里加入上述的定义。
4.3.3.3 同时生成同名的动态库和静态库
- 错误的做法
lib/CMakeLists.txt:
SET(LIBHELLO_SRC hello.c)
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
ADD_LIBRARY(hello STATIC ${LIBHELLO_SRC})
在 build 目录进行外部编译,我们会发现,静态库根本没有被构建,仍然只生成了 一个动态库。因为 hello 作为一个 target 是不能重名的,所以,静态库构建指令无效。
如果我们把上面的 hello 修改为 hello_static: ADD_LIBRARY(hello_static STATIC ${LIBHELLO_SRC}) 就可以构建一个 libhello_static.a 的静态库了。
这种结果显示不是我们想要的,我们需要的是名字相同的静态库和动态库,因为 target 名 称是唯一的,所以,我们肯定不能通过 ADD_LIBRARY 指令来实现了。
添加下面这条语句,就可以生成 libhello.so/libhello.a 两个库了
SET_TARGET_PROPERTIES(hello_static PROPERTIES OUTPUT_NAME "hello")
- 使用SET_TARGET_PROPERTIES设置名称对象的相关属性
SET_TARGET_PROPERTIES(target1 target2 …
PROPERTIES prop1 value1
prop2 value2 …)
SET_TARGET_PROPERTIES(hello_static PROPERTIES OUTPUT_NAME "hello")
- 设置输出的名称
- 防止生成libhello.a的时候删除libhello.so
libhello.a 已经构建完成,位于 build/lib 目录中,但是 libhello.so 去消失了。这个问题的原因是:cmake 在构建一
个新的 target 时,会尝试清理掉其他使用这个名字的库,因为,在构建 libhello.a 时, 就会清理掉 libhello.so.
为了回避这个问题,比如再次使用 SET_TARGET_PROPERTIES 定义 CLEAN_DIRECT_OUTPUT 属性。
向 lib/CMakeLists.txt 中添加:
SET_TARGET_PROPERTIES(hello PROPERTIES CLEAN_DIRECT_OUTPUT 1)
SET_TARGET_PROPERTIES(hello_static PROPERTIES CLEAN_DIRECT_OUTPUT
这时候,我们再次进行构建,会发现 build/lib 目录中同时生成了 libhello.so 和 libhello.a
-
对于动态库,还可以用来指定动态库版本和 API 版本。
SET_TARGET_PROPERTIES(hello PROPERTIES VERSION 1.2 SOVERSION 1)
VERSION 指代动态库版本,SOVERSION 指代 API 版本。
将上述指令加入 lib/CMakeLists.txt 中,重新构建看看结果。 在 build/lib 目录会生成:
libhello.so.1.2
libhello.so.1->libhello.so.1.2
libhello.so ->libhello.so.1
- 设置输出的名称
- 使用GET_TARGET_PROPERTY获取设置的名称属性
GET_TARGET_PROPERTY(VAR target property)
GET_TARGET_PROPERTY(OUTPUT_VALUE hello_static OUTPUT_NAME)
MESSAGE(STATUS “This is the hello_static OUTPUT_NAME:”${OUTPUT_VALUE})
如果没有这个属性定义,则返回 NOTFOUND.
4.4 配置安装文件存储路径
INSTALL 指令用于定义安装规则,安装的内容可以包括目标二进制、动态库、静态库以及 文件、目录、脚本等。
4.4.1 目标文件的安装(目标二进制、动态库、静态库)
INSTALL(TARGETS targets…
[[ARCHIVE|LIBRARY|RUNTIME]
[DESTINATION <dir>]
[PERMISSIONS permissions…]
[CONFIGURATIONS
[Debug|Release|…]]
[COMPONENT <component>]
[OPTIONAL]
] […])
4.4.1.1 install语句位置
位于生成目标的所在 CMakeLists.txt中
4.4.1.2 设置目标文件名
TARGETS后面跟的就是我们通过 ADD_EXECUTABLE 或者 ADD_LIBRARY 定义的 目标文件,可能是可执行二进制、动态库、静态库。
4.4.1.3 设置目标类型
目标类型也就相对应的有三种
ARCHIVE | 静态库 |
LIBRARY | 动态库 |
RUNTIME | 可执行目标二进制 |
4.4.1.4 设置安装路径
DESTINATION 定义了相对于变量CMAKE_INSTALL_PREFIX的安装路径
- 默认路径
CMAKE_INSTALL_PREFIX 的默认定义是/usr/local
- 绝对路径
如果路径以/开头,那么指的是绝对路径,这时候 CMAKE_INSTALL_PREFIX 其实就无效了。
- 相对路径
如果你希望使用 CMAKE_INSTALL_PREFIX 来定义安装路径,就要写成相对路径,即不要以/开头,那么安装后的路径就是${CMAKE_INSTALL_PREFIX}/<DESTINATION 定义的路径>
4.4.1.5 例子1
INSTALL(TARGETS myrun mylib mystaticlib
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION libstatic
)
可执行二进制 myrun 安装到
CMAKEINSTALLPREFIX/bin目录动态库libmylib安装到
{CMAKE_INSTALL_PREFIX}/lib 目录
静态库 libmystaticlib 安装到${CMAKE_INSTALL_PREFIX}/libstatic 目录
4.4.1.6 例子2
工程文件夹/lib(源码)/CMakeLists.txt中
INSTALL(TARGETS hello hello_static
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib)
INSTALL(FILES hello.h DESTINATION include/hello)
cmake -DCMAKE_INSTALL_PREFIX=/usr ..
make
make install
我们就可以将头文件和共享库安装到系统目录/usr/lib 和/usr/include/hello 中了。
4.4.2 普通文件的安装
INSTALL(FILES files… DESTINATION <dir>
[PERMISSIONS permissions…]
[CONFIGURATIONS [Debug|Release|…]]
[COMPONENT <component>]
[RENAME <name>] [OPTIONAL])
4.4.2.1 install语句位置
任意的 CMakeLists.txt中,使用路径寻找源文件
INSTALL(FILES "${PROJECT_SOURCE_DIR}/README" DESTINATION zcl)
4.4.2.2 默认权限(不指定PERMISSIONS)
安装后的权限为OWNER_WRITE, OWNER_READ, GROUP_READ,和WORLD_READ,即644权限。
4.4.2.3 例子
将COPYRIGHT/README 安装到 /<prefix>/share/doc/cmake/t2
- 添加工程目录中的 COPYRIGHT 和 README touch COPYRIGHT touch README
- 修改主工程文件 CMakelists.txt,加入以下指令: INSTALL(FILES COPYRIGHT README DESTINATION share/doc/cmake/t2)
4.4.3 非目标文件的可执行程序安装(比如脚本之类):
INSTALL(PROGRAMS files… DESTINATION <dir>
[PERMISSIONS permissions…]
[CONFIGURATIONS [Debug|Release|…]]
[COMPONENT <component>]
[RENAME <name>] [OPTIONAL])
4.4.3.1 跟上面的 FILES 指令使用方法一样,唯一的不同是安装后权限为
OWNER_EXECUTE, GROUP_EXECUTE, 和WORLD_EXECUTE,即755权限
4.4.3.2 例子
- 在工程目录添加 runhello.sh 脚本,内容为:hello
- 修改主工程文件 CMakeLists.txt,加入INSTALL(PROGRAMS runhello.sh DESTINATION bin)
4.4.4 目录的安装:
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…]] […])
4.4.4.1 所在 Source 目录的相对路径
DIRECTORY 但务必注意:abc 和 abc/有很大的区别。
如果目录名不以/结尾,那么这个目录将被安装为目标路径下的 abc,如果目录名以/结尾, 代表将这个目录中的内容安装到目标路径,但不包括这个目录本身。
4.4.4.2 PATTERN 用于使用正则表达式进行过滤
4.4.4.3 PERMISSIONS 用于指定 PATTERN 过滤后的文件权限
4.4.4.4 例子1
INSTALL(DIRECTORY icons scripts/ DESTINATION share/myproj
PATTERN "CVS" EXCLUDE
PATTERN "scripts/*"
PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ)
这条指令的执行结果是:
将icons目录安装到 <prefix>/share/myproj,将scripts/中的内容安装到<prefix>/share/myproj
不包含目录名为CVS的目录,对于scripts/*文件指定权限为 OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ.
4.4.4.5 例子2
- 工程路径添加 doc 目录及文件:
cd /backup/cmake/t2
mkdir doc
vi doc/hello.txt
随便填写一些内容并保存
- 安装 doc 中的 hello.txt,这里有两种方式
- 通过在 doc 目录建立 CMakeLists.txt 并将 doc 目录通过 ADD_SUBDIRECTORY 加入工程来完成。
- 直接在工程目录通过INSTALL(DIRECTORY 来完成)
hello.txt 要安装到/<prefix>/share/doc/cmake/t2,所以我们不能直接安装 整个 doc 目录,这里采用的方式是安装 doc 目录中的内容,也就是使用”doc/”
在工程文件中添加INSTALL(DIRECTORY doc/ DESTINATION share/doc/cmake/t2)
- 通过在 doc 目录建立 CMakeLists.txt 并将 doc 目录通过 ADD_SUBDIRECTORY 加入工程来完成。
4.4.4.6 安装文件夹到目的位置,并将文件夹改名
INSTALL(DIRECTORY include/ DESTINATION includeinstallpath/
{include_install_name})
结果: 将文件夹include安装到目的位置,并将include文件改名
4.4.5 安装时 CMAKE 脚本的执行:
INSTALL([[SCRIPT <file>] [CODE <code>]] […])
SCRIPT 参数用于在安装时调用 cmake 脚本文件(也就是<abc>.cmake 文件)
CODE 参数用于执行 CMAKE 指令,必须以双引号括起来。比如: INSTALL(CODE "MESSAGE(\"Sample install message.\")")
4.4.6 安装结果make install
现在进入 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
如果你要直接安装到系统,可以使用如下指令:
cmake -DCMAKE_INSTALL_PREFIX=/usr ..
5 第二部分:工程文件夹组织
5.1 工程的 CMakeLists.txt文件
PROJECT(HELLO)
ADD_SUBDIRECTORY(src bin)
5.2 src文件夹
- 一些工程源代码
- 一个 CMakeLists.txt文件(必须)
ADD_EXECUTABLE(hello main.c)
5.3 doc文件夹
放置这个工程的文档 hello.txt
5.4 文本文件COPYRIGHT, README
5.5 runhello.sh 脚本
用来调用hello二进制
5.6 build构建文件夹
bin 子目录(无需创建,自动生成),存放构建后的目标文件
5.7 最终安装这些文件:将 hello 二进制与 runhello.sh 安装至/usr/bin,将 doc 目录 的内容以及 COPYRIGHT/README 安装到/usr/share/doc/cmake/t2
6 第三部分:使用cmake构建程序
6.1 开始构建
6.1.1 外部构建(推荐使用)
6.1.1.1 优点
对于原有的工程没有任何影响,所有动作全部发生在编译目录。
6.1.1.2 开始构建
$cd 进入 build 目录
$cmake .. //..为工程 CMakeLists.txt文件所在位置
$make
结果: 在build目录下生成bin目录,里面含有可执行文件
6.1.1.3 外部编译的过程
- 首先,请清除t1目录中除main.c CmakeLists.txt之外的所有中间文件,最关键的是CMakeCache.txt。
- 在t1目录中建立build 目录,当然你也可以在任何地方建立build目录,不一定必 须在工程目录中。
- 进入build目录,运行cmake ..(注意,..代表父目录,因为父目录存在我们需要的 CMakeLists.txt,如果你在其他地方建立了build目录,需要运行cmake <工程的全 路径>),查看一下 build 目录,就会发现了生成了编译需要的 Makefile 以及其他的中间 文件。
- 运行 make 构建工程,就会在当前目录(build 目录)中获得目标文件hello。
6.1.2 内部构建
6.1.2.1 缺点
生成了一些无法自动删除的中间文件
生成的临时文件比您的代码文件还要多的时候,估计这辈子你都不希望再使用内部构建
6.1.2.2 开始构建
cmake . //.表示当前目录
你会发现,系统自动生成了:
CMakeFiles, CMakeCache.txt, cmake_install.cmake等文件,并且生成了 Makefile.
现在不需要理会这些文件的作用,以后你也可以不去理会。最关键的是,它自动生成了Makefile.
6.2 安装文件(拷贝)
6.2.1 方法一:代码编译后直接make install安装
6.2.2 方法二:打包时的指定目录安装。
6.2.3 一个非常有用的变量
CMAKE_INSTALL_PREFIX,CMAKE_INSTALL_PREFIX变量类似于configure脚本的 –prefix。
常见的使用方法看起来是这个样子:
cmake -DCMAKE_INSTALL_PREFIX=/usr .
6.2.4 例子
- 为工程添加一个子目录 src,用来存储源代码;
- 添加一个子目录 doc,用来存储这个工程的文档 hello.txt
- 在工程目录添加文本文件COPYRIGHT, README;
- 在工程目录添加一个 runhello.sh 脚本,用来调用 hello 二进制
- 将构建后的目标文件放入构建目录的 bin 子目录;
- 最终安装这些文件:将 hello 二进制与 runhello.sh 安装至/<prefix>/bin。将 doc 目录中的 hello.txt 以及 COPYRIGHT/README 安装到 /<prefix>/share/doc/cmake/t2
6.2.5 其他方案
6.2.5.1 Makefile
最简单的手工编写的 Makefile,看起来也是这个样子的: DESTDIR=
install:
mkdir -p $(DESTDIR)/usr/bin
install -m 755 hello $(DESTDIR)/usr/bin
你可以通过: make install
将 hello 直接安装到/usr/bin 目录,也可以通过 make install DESTDIR=/tmp/test 将他安装在
/tmp/test/usr/bin 目录,打包时这个方式经常被使用。
6.2.5.2 autotools 工程
./configure –prefix=/usr或者./configure –prefix=/usr/local来指定 PREFIX
比如上面的 Makefile 就可以改写成: DESTDIR=
PREFIX=/usr
install:
mkdir -p
(DESTDIR)/
install -m 755 hello (DESTDIR)/
(PREFIX)/bin
6.3 清理构建结果
只执行make clean是不会清空构建结果的,有些cmake官方定义的变量和用户执行cmake时传入的变量会记录在 CMakeCache.txt中。因此应该为:
- make clean
- rm CMakeCache.txt //最好是rm build
6.4 运行程序
./hello