CMakeLists
CMake是跨平台编译工具,比make更高级一些。其编译的主要工作是生成CMakeLists.txt文件,然后根据该文件生成Makefile,最后调用make来生成可执行程序或者动态库。所以基本步骤就只有两步
- cmake生成Makefile件
- make执行编译工作
1. 常用语法
1.1. 消息输出
message("Hello world!")
1.2. 设置变量
cmake 并没有类,但是我们可以通过定义一组变量,该组变量以相同的前缀来模拟类,并使用嵌套的 ${}
来引用这些变量。例如:
set(LEO_NAME "Leo Lee")file(GLOB_RECURSE FLANN_LIBRARIES ${THIRDPARTY}/flann-1.9.1/lib/lib*)
其中
set用于显示地指定变量,它是静态的,在语句中写的是什么就是什么;
file是动态的,它到系统路径中去找文件,找到了即进行赋值。
1.3. 算术运算
math(EXPR MY_SUM "1 + 1") math(EXPR DOUBLE_SUM "${MY_SUM} * 2")
1.4. 流程控制命令
流程控制命令包括 if/endif
和 while/endwhile
。
如,判断当前环境是否为 win32:
if(WIN32)message("You're running CMake on Windows.")
else ()message("You aren't running CMake on Windows.")
endif()
例如,使用 while/endwhile
循环打印出所有小于 20 的斐波那契数列:
set(A "1")
set(B "1")
while(${A} LESS "20")message("${A}") # 打印 Amath(EXPR T "${A} + ${B}") # 计算 A + B 的值,并存储在变量 Tset(A "${B}") # 设置变量 A 的值为 Bset(B "${T}") # 设置变量 B 的值为 T
endwhile()
在 cmake 中,变量与数字进行比较的语法与其他语言不同,例如上面的小于比较,就使用了 LESS
,其他比较条件的使用。
输出:
1
1
2
3
5
8
13
1.5. 列表:分号分隔的字符串
直接以例子说明。以下例子中,将三个参数传递给 math
:
set(ARGS "EXPR;T;1 + 1")
math(${ARGS}) # 等价于 math(EXPR T "1 + 1")
message(${T})
如果引用变量 ${}
外部添加双引号,则 cmake 会将整个字符串作为一个参数,并保留分号:
set(ARGS "EXPR;T;1 + 1")
message("${ARGS}")
输出:
EXPR;T;1 + 1
如果 ${}
不带双引号,例如:
set(ARGS "EXPR;T;1 + 1")
message(${ARGS})
则输出:
EXPRT1 + 1
如果有两个以上的参数传递给 set
命令,则它们会被分号连接,然后传递给指定的变量:
set(MY_LIST Please visit leehao.me)
message("${MY_LIST}")
输出:
Please;visit;leehao.me
可以使用 list 命令处理列表:
set(MY_LIST Please visit url leehao.me)
list(REMOVE_ITEM MY_LIST "url")
message("${MY_LIST}")
输出:
Please;visit;leehao.me
可以使用 foreach/endforeach
处理列表,迭代除第一个参数外的列表的所有项,并将每项赋值给第一个参数变量:
foreach(ARG Please visit url leehao.me)message("${ARG}")
endforeach()
输出:
Please
visit
url
leehao.me
1.6. 定义函数
在 cmake 中,可以使用 function/endfunction
来定义一个函数,例如下面的函数将参数值 * 2 后输出:
function(doubleIt VALUE)math(EXPR RESULT "${VALUE} * 2")message("${RESULT}")
endfunction()doubleIt("4")
输出:
8
函数中定义的变量不会影响调用方的作用域,如果需要返回值,可以将变量传递给函数,然后使用 set
命令,并指定 PARENT_SCOPE
参数:
function(doubleIt VARNAME VALUE)math(EXPR RESULT "${VALUE} * 2")set(${VARNAME} "${RESULT}" PARENT_SCOPE) # 设置返回值
endfunction()doubleIt(RESULT "4") # RESULT 变量存储函数的返回值
message("${RESULT}") # 输出:8
cmake 中,使用 macro/endmacro
定义宏。与函数不同,宏内改变变量的值会影响调用方的作用域:
macro(doubleIt VARNAME VALUE)math(EXPR ${VARNAME} "${VALUE} * 2")
endmacro()doubleIt(RESULT "4")
message("${RESULT}")
1.7. CMAKE_CXX_FLAGS设置
# set compiler
# -O2 or -O3 优化
# -fPIC 产生与位置无关代码
# -g 调试信息
# -Wall 开启所有的编译警告
# -ffast-math -ffast-math -use_fast_math 浮点数计算加速
# -fdiagnostics-color 颜色设置
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2 -fPIC -std=c++14 -g -Wall -ffast-math -ffast-math -use_fast_math -fdiagnostics-color=auto")
1.8. 警告
- -w
禁止编译警告的打印。这个警告不建议使用。大约2012年底,公司代码进行一次大重构,另外从Codeblock集成开发环境转向Makefile管理,Makefile里面默认使用了-w,因而代码一直没有警告,今年个别项目开发中发现一些代码笔误导致的BUG,而这些问题可以从编译警告中知道。前几个月,领导安排我来fix这些警告。为了自己,为了后人,不建议使用-w选项。
- -Werror
将所有的警告当成错误处理。此选项谨慎建议加上。有的开源库警告很多(大名鼎鼎的ffmpeg也有很多警告呢),一一改掉耗时耗人力,必要性也不大。最后,公司代码加入了一个开源库,里面有很多代码警告,可能领导又安排我来fix了。
- -Wfatal-errors
遇到第一个错误就停止,减少查找错误时间。建议加上。很多人遇到错误,没有意识到从第一个开始排查。不管是编译错误,还是程序运行出错,从最开始的错误查起,是个好的做法。
-Wall开启“所有”的警告。强烈建议加上,并推荐该选项成为共识。如case语句没有default处理,有符号、无符号处理,未使用变量(特别是函数有大量未使用的数组,占用栈空间,测试发现,开辟一个未使用的8MB的数组,程序有coredump),用%d来打印地址,或%s打印int值等,都可以发出警告。
- -Wextra
除-Wall外其它的警告。建议加上。
在GCC编译时,加上必要的警告选项,可以避免很多低级错误引发的问题,我就在实际工程代码中遇到用“==”来赋值,我自己写的代码也出现过把“=”当成判断的。但是,有些错误却不是用GCC选项能解决的。比如一般项目都会自定义调试信息打印函数,但在处理可变参数类型时,往往不注意。
1.9. 位置无关代码
PIC就是position independent code,告诉编译器产生与位置无关代码。即产生的代码中,没有绝对地址,全部使用相对地址,故而代码可以被加载器加载到内存的任意位置,都可以正确的执行。这正是共享库所要求的,共享库被加载时,在内存的位置不是固定的。
PIC使.so文件的代码段变为真正意义上的共享。如果不加-fPIC,则加载.so文件的代码段时,代码段引用的数据对象需要重定位,重定位会修改代码段的内容,这就造成每个使用这个.so文件代码段的进程在内核里都会生成这个.so文件代码段的copy.每个copy都不一样,取决于这个.so文件代码段和数据段内存映射的位置。
不加fPIC编译出来的so,是要再加载时根据加载到的位置再次重定位的.(因为它里面的代码并不是位置无关代码)
如果被多个应用程序共同使用,那么它们必须每个程序维护一份so的代码副本了.(因为so被每个程序加载的位置都不同,显然这些重定位后的代码也不同,当然不能共享)
我们总是用fPIC来生成so,也从来不用fPIC来生成a.
fPIC与动态链接可以说基本没有关系,libc.so一样可以不用fPIC编译,只是这样的so必须要在加载到用户程序的地址空间时重定向所有表目。
因此,不用fPIC编译so并不总是不好。
如果你满足以下4个需求/条件:
- 该库可能需要经常更新
- 该库需要非常高的效率(尤其是有很多全局量的使用时)
- 该库并不很大.
- 该库基本不需要被多个应用程序共享
如果用没有加这个参数的编译后的共享库,也可以使用的话,可能是两个原因:
- gcc默认开启-fPIC选项
- loader使你的代码位置无关
从GCC来看,shared应该是包含fPIC选项的,但似乎不是所以系统都支持,所以最好显式加上fPIC选项。
1.10. 链接
target_link_libraries(hello A B.a C.so)
在上面的命令中,libA.so可能依赖于libB.a和libC.so,如果顺序有错,链接时会报错。还有一点,B.a会告诉CMake优先使用静态链接库libB.a,C.so会告诉CMake优先使用动态链接库libC.so,也可直接使用库文件的相对路径或绝对路径。使用绝对路径的好处在于,当依赖的库被更新时,make的时候也会重新链接。
gcc中库的链接顺序是从右往左进行,所以要把最基础实现的库放在最后,这样左边的lib就可以调用右边的lib中的代码。同时,当一个函数的实现代码在多个lib都存在时,最左边的lib代码最后link,所以也将最终保存下来。
2. RPATH
2.1. 什么是RPATH
在Linux环境下,使用动态链接的程序在运行时会自动链接 ld.so
这个库(OS X上是 dyld
),然后通过 ld.so
来查找链接其它的库。而 RPATH
就是编译的时候链接到执行文件的链接库路径。OS X在 RPATH
的设置上和Linux还是有点出入的,OS X的 RPATH
采用的是绝对路径。
ld.so
搜索路径的优先级是这样的:
RPATH
,编译链接时加入-rpath
参数指明所谓的RUNPATH
,这样可执行文件(或者依赖其他动态链接库的动态链接库)就能告诉ld.so
到哪里去搜索对应的动态链接库了。LD_LIBRARY_PATH
,对于没有设定RPATH
的可执行文件或者动态链接库,我们可以用LD_LIBRARY_PATH
这个环境变量通知ld.so
往哪里查找链接库。/etc/ld.so.conf
,系统对ld.so
的路径配置文件。/usr/lib
、/lib
和/usr/local/lib
,系统默认路径。
2.2. Cmake和RPATH
在分发程序的时候,执行文件使用的链接库在系统内不一定会有,或者自带了的版本不对,一般都会在程序文件夹内都会附带相应的链接库,所以最好还是把 RPATH
加上。Cmake对RPATH提供了很多选项支持,我们一般只关注这几个变量就好了: CMAKE_SKIP_BUILD_RPATH
、 CMAKE_BUILD_WITH_INSTALL_RPATH
、 CMAKE_INSTALL_RPATH
和 CMAKE_INSTALL_RPATH_USE_LINK_PATH
。
2.3. 默认RPATH设置
set(CMAKE_SKIP_BUILD_RPATH FALSE) # 编译时加上RPATH
set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) # 编译时RPATH不使用安装的RPATH
set(CMAKE_INSTALL_RPATH "") # 安装RPATH为空
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH FALSE) # 安装的执行文件不加上RPATH
Cmake在默认情况下, make install
会把安装的执行文件的 RPATH
删掉的,所以就会出现上面我执行安装好的执行文件报错的问题。
2.4. 加上完整的RPATH
Cmake的默认设置我们肯定是不能使用的,我们需要一个安装的时候也要带上 RPATH
的设置
set(INSTALL_LIB_DIR "${PROJECT_BINARY_DIR}/lib") # 假设安装目录在编译目录的lib子目录内set(CMAKE_SKIP_BUILD_RPATH FALSE)
set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)# 确保链接库不在系统默认安装的目录上时更改到项目lib上
list(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES ${CMAKE_INSTALL_RPATH} isSystemDir)
if("${isSystemDir}" STREQUAL "-1") set(CMAKE_INSTALL_RPATH "${INSTALL_LIB_DIR}")
endif("${isSystemDir}" STREQUAL "-1")
3. 示例
project(test) # version requirement
cmake_minimum_required(VERSION 2.8)set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2 -fPIC -std=c++14 -g -Wall -ffast-math -ffast-math -use_fast_math -fdiagnostics-color=auto")# set path of cmake files
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/modules)# set path of executable files
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)# set path of library
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)# find third party libraries
find_package(Boost COMPONENTS thread filesystem date_time system REQUIRED)
find_package(Eigen3 REQUIRED)
find_package(OpenCV REQUIRED)
find_package(glog REQUIRED)
find_package(GTSAM REQUIRED)
find_package(Ceres REQUIRED)# report path of third party libraries
message(${EIGEN3_INCLUDE_DIR})# header files
include_directories(${Boost_INCLUDE_DIR})
include_directories(${EIGEN3_INCLUDE_DIR})
include_directories(${OpenCV_INCLUDE_DIRS})
include_directories(${CERES_INCLUDE_DIRS})
include_directories(include)# set source files
AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/src SOURCE_FILES)add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES})
target_link_libraries( ${PROJECT_NAME}${PCL_LIBRARIES}${Boost_LIBRARIES}${OpenCV_LIBS}glog${libLAS_LIBRARIES}
)add_executable(${PROJECT_NAME} main/test.cpp)
# link libraries
target_link_libraries(${PROJECT_NAME} ${Boost_LIBRARIES} glog ${CERES_LIBRARIES})
如果需要链接自己在其它工程下编译的链接库,则需加下下列代码
# 指定链接库目录,链接库的名为common
link_directories(${CMAKE_SOURCE_DIR}/../common/lib/)
link_libraries(common.so)set(${PROJECT_NAME}common
)
参考文献
CMake 语言 15 分钟入门教程_Leo的博客-CSDN博客_cmake语言
gcc编译参数-fPIC的一些问题
GCC编译警告选项的学习_李迟的专栏-CSDN博客
[CMAKE] 详解CMakeLists.txt文件 - VictoKu - 博客园
CMake命令target_link_libraries链接库的顺序_zhujianwei31415的专栏-CSDN博客_target_link_libraries
CMAKE和RPATH_章志强的专栏-CSDN博客_cmake rpath
CMakeLists相关推荐
- TVM示例展示 README.md,Makefile,CMakeLists.txt
TVM示例展示 README.md,Makefile,CMakeLists.txt TVM/README.md Open Deep Learning Compiler Stack Documentat ...
- CMakeLists.txt学习记录
一.Cmake 学习地址与作用 cmake详细见:https://gitlab.kitware.com/cmake/community/-/wikis/home 是一个跨平台.开源的构建系统.它是一个 ...
- 一文详解CMakeLists文件编写语法规则详解
作者丨zhanghm1995@blog 来源丨https://blog.csdn.net/zhanghm1995/article/details/80902807 编辑丨3D视觉工坊 基本语法规则 C ...
- CMakeLists.txt从入门到精通
文章目录 前言 一 一般工程开头的一些设置案例 二 动态库与静态库的生成 2.1 动态库 2.2 静态库 三 优化选项的设置 四 生成库时的设置 五 常见依赖库的调用 5.1 OpenCV库 六 设置 ...
- CMakeLists.txt文件如何编写?(一 基础篇)
本文首发于微信公众号「3D视觉工坊」--CMakeLists.txt文件如何写? 本文以linux平台下CMakeLists.txt文件书写方法总结. 一 开头通用模块 1.1 cmake版本要求 c ...
- “cmake 点点”表示在上一级目录(CMakeLists.txt所在目录)编译
CMake学习(一)_福尔摩斯帅双的博客-CSDN博客 在 linux 平台下使用 CMake 生成 Makefile 并编译的流程如下: 编写 CMake 配置文件 CMakeLists.txt . ...
- Qt导入CMakeLists.txt后无法调试
问题: Qt导入CMakeLists.txt后无法单步调试 解决方法: 在CMakeLists.txt后加入一句: SET(CMAKE_BUILD_TYPE DEBUG) 转载于:https://ww ...
- CMakeLists.txt的运行方法
在学习pcl的时候,包括在别的一些学习过程中,我们经常会遇到书中提供的一些源码,但是我们不知道怎么运行,就像下面的一些代码 之前一直使用的方法就是创建VS工程,把CPP文件包含进去,然后运行,但是既然 ...
- CMakeLists.txt
示例(在编写CMakeLists.txt前, 创建好src, include, lib, bin, build目录, 其中cmake ..在build目录中执行) # 一般都会先声明cmake的版本 ...
最新文章
- 2012 iis php mysql_Win2012 R2 IIS8.5+PHP(FastCGI)+MySQL运行环境搭建wordpress博客教程
- 如何编写oracle存储过程
- Linux服务器CPU、内存、磁盘空间、负载情况查看python脚本
- Vmware快速安装linux虚拟机(SUSE)
- SAP Commerce的Content Page,Content Slot和Component在Spartacus里的应用
- 帝国CMS7.5会员中心美化版V1.0GBKamp;UTF
- matlab转换为exe文件,MATLAB文件转为exe可执行文件(package使用)
- centos中安装配置nginx完成之后主机无法访问
- orcad中的PSpice仿真加入厂商模型
- Python入门学习—元组/字符串(FishC)
- 在线PPT—Sway初级教程
- android 蓝牙串口指令,蓝牙串口助手
- Linux环境安装ghostscript-9.25
- 输入等值线参数绘制等值线图python_专题复习:等值线(上)
- 外贸电子商务常见VISA信用卡或MasterCard等测试信用卡卡号一览
- MT【33】证明琴生不等式
- 计算机上怎么计算x的n次方,计算x的n次方(用函数)
- 上面两点下面一个三角形_把握字的形状,即使写得快,也很好看(三角形2)...
- 在职计算机培训班,计算机科学与技术在职研究生招生院校有哪些?
- ssm项目---人事管理系统:员工与部门、职位实现一对一
热门文章
- Javascript 中的神器——Promise
- 1112. Stucked Keyboard (20)
- shell中大于、等于、小于
- Tempdb数据库详细介绍
- 点滴积累【C#】---检验编号在本表中自动生成,与其他表无关
- android开发我的新浪微博客户端-登录页面功能篇(4.2)
- Ansible04-任务控制
- 我对Node.js Core的首次贡献中学到了什么
- 矩阵专职_新的篇章开始了-我将以专职技术作家的身份加入RunCloud
- react前端开发_是的,React正在接管前端开发。 问题是为什么。