CMake学习笔记

什么是CMake

你或许听过好几种 Make 工具,例如 GNU Make ,QT 的 qmake ,微软的 MS nmake,BSD Make(pmake),Makepp,等等。这些 Make 工具遵循着不同的规范和标准,所执行的 Makefile 格式也千差万别。这样就带来了一个严峻的问题:如果软件想跨平台,必须要保证能够在不同平台编译。而如果使用上面的 Make 工具,就得为每一种标准写一次 Makefile ,这将是一件让人抓狂的工作。

CMake 就是针对上面问题所设计的工具:它首先允许开发者编写一种平台无关的 CMakeList.txt 文件来定制整个编译流程,然后再根据目标用户的平台进一步生成所需的本地化 Makefile 和工程文件,如 Unix 的 Makefile 或 Windows 的 Visual Studio 工程。从而做到“Write once, run everywhere”。显然,CMake 是一个比上述几种 make 更高级的编译配置工具。一些使用 CMake 作为项目架构系统的知名开源项目有 VTK、ITK、KDE、OpenCV、OSG 等 。

入门案例:单个源文件的内部构建和外部构建

CMake外部构建

对于简单的项目,只需要写几行代码就可以了。例如,假设现在我们的项目中只有一个源文件 main.cpp 。

#include <stdio.h>
int main()
{
printf("Hello World from t1 Main!\n");
return 0;
}

1.编写CMakeList.txt

CMakeLists.txt 的语法比较简单,由命令、注释和空格组成,其中命令是不区分大小写的(推荐全部使用大写指令)。符号 # 后面的内容被认为是注释。命令由命令名称、小括号和参数组成,参数之间使用空格进行间隔。(指令(参数1 参数 2…))。变量使用${}方式取值,但是在IF控制语句中是直接使用变量名。

首先编写 CMakeLists.txt 文件,并保存在与 main.cc 源文件同个目录下:

PROJECT (HELLO)
SET(SRC_LIST main.cpp)
MESSAGE(STATUS "This is BINARY dir " ${HELLO_BINARY_DIR})
MESSAGE(STATUS "This is SOURCE dir "${HELLO_SOURCE_DIR})
ADD_EXECUTABLE(hello ${SRC_LIST})

2.生成CMakeFiles

  1. 执行命令 cmake PATH 或者 ccmake PATH 生成 Makefile(ccmake 和 cmake 的区别在于前者提供了一个交互式的界面)。其中, PATH 是 CMakeLists.txt 所在的目录。
  2. 使用 make 命令进行编译。

在这里插入图片描述

在这里插入图片描述

  • 指令解释

  • PROJECT指令的语法:
    PROJECT(projectname [CXX] [C] [Java])
    你可以用这个指令定义工程名称,并可指定工程支持的语言,支持的语言列表是可以忽略的,默认情况表示支持所有语言。这个指令隐式的定义了两个 cmake 变量:
    < projectname>_BINARY_DIR以及< projectname>_SOURCE_DIR,这里就是HELLO_BINARY_DIR和HELLO_SOURCE_DIR(所以CMakeLists.txt中两个MESSAGE指令可以直接使用了这两个变量),因为采用的是内部编译,两个变量目前指的都是工程所在路径,后面我们会讲到外部编译,两者所指代的内容会有所不同。
    在这里插入图片描述

    同时cmake 系统也帮助我们预定义了 PROJECT_BINARY_DIR和PROJECT_SOURCE_DIR变量,他们的值分别跟HELLO_BINARY_DIR与HELLO_SOURCE_DIR一致。
    为了统一起见,建议以后直接使用PROJECT_BINARY_DIR,PROJECT_SOURCE_DIR,即使修改了工程名称,也不会影响这两个变量。如果使用了< projectname>_SOURCE_DIR ,修改工程名称后,需要同时修改这些变量。

  • SET指令的语法:
    SET(VAR [VALUE] [CACHE TYPE DOCSTRING [FORCE]])
    SET指令可以用来显式的定义变量。
    比如我们用到的是SET(SRC_LIST main.c),如果有多个源文件,也可以定义成:SET(SRC_LIST main.c t1.c t2.c)

  • MESSAGE指令的语法:
    MESSAGE([SEND_ERROR | STATUS | FATAL_ERROR] “message to display”…)
    这个指令用于向终端输出用户定义的信息,包含了三种类型:
    SEND_ERROR,产生错误,生成过程被跳过
    SATUS — ,输出前缀为 的信息
    FATAL_ERROR,立即终止所有cmake 过程
    我们在这里使用的是STATUS 信息输出,演示了由PROJECT 指令定义的两个隐式变量HELLO_BINARY_DIR和HELLO_SOURCE_DIR。

  • ADD_EXECUTABLE(hello ${SRC_LIST})
    定义了这个工程会生成一个文件名为hello 的可执行文件,相关的源文件是 SRC_LIST 中定义的源文件列表, 本例中也可以直接写成ADD_EXECUTABLE(hello main.c)

在本例我们使用了$ {}来引用变量,这是cmake 的变量应用方式,但是,有一些例外,比如在IF 控制语句,变量是直接使用变量名引用,而不需要$ {}。如果使用了$ {}去应用变量,其实IF 会去判断名为${}所代表的值的变量,那当然是不存在的了。

将本例改写成一个最简化的CMakeLists.txt:

PROJECT(HELLO)
ADD_EXECUTABLE(hello main.c)

cmake 的语法还是比较灵活而且考虑到各种情况,比如SET(SRC_LIST main.c)也可以写成 SET(SRC_LIST “main.c”)是没有区别的,但是假设一个源文件的文件名是 fu nc.c(文件名中间包含了空格)。这时候就必须使用双引号,如果写成了 SET(SRC_LIST fu nc.c),就会出现错误,提示你找不到fu 文件和nc.c 文件。这种情况,就必须写成:SET(SRC_LIST “fu nc.c”)

此外,你可以可以忽略掉source 列表中的源文件后缀,比如可以写成ADD_EXECUTABLE(t1 main),cmake 会自动的在本目录查找main.c或者main.cpp等,当然,最好不要偷这个懒,以免这个目录确实存在一个 main.c 一个main

更像工程的一些改进

  1. 为工程添加一个子目录src,用来放置工程源代码;
  2. 添加一个子目录doc,用来放置这个工程的文档hello.txt
  3. 在工程目录添加文本文件COPYRIGHT, README;
  4. 在工程目录添加一个runhello.sh 脚本,用来调用hello二进制
  5. 将构建后的目标文件放入构建目录的bin 子目录;
  6. 最终安装这些文件:将hello二进制与runhello.sh安装至/usr/bin,将doc目录的内容以及COPYRIGHT/README安装到/usr/share/doc/cmake/t2

步骤

  1. 添加子目录src
    在这里插入图片描述
  2. 进入子目录src,编写 CMakeLists.txt:ADD_EXECUTABLE(hello main.c)
  3. 将原工程的 CMakeLists.txt修改为:
    PROJECT(HELLO)
    ADD_SUBDIRECTORY(src bin)
  4. 然后建立build 目录,进入build目录进行外部编译。
    cmake . .
    make
  5. 构建完成后,你会发现生成的目标文件 hello 位于 build/bin目录中
    在这里插入图片描述

语法解释:

  • ADD_SUBDIRECTORY指令
    ADD_SUBDIRECTORY(source_dir [binary_dir] [EXCLUDE_FROM_ALL])这个指令用于向当前工程添加存放源文件的子目录,并可以指定中间二进制和目标二进制存放的位置。EXCLUDE_FROM_ALL参数的含义是将这个目录从编译过程中排除,比如,工程的example,可能就需要工程构建完成后,再进入 example目录单独进行构建(当然,你也可以通过定义依赖来解决此类问题)。

    上面的例子定义了将src子目录加入工程,并指定编译输出(包含编译中间结果)路径为bin目录。如果不进行 bin目录的指定,那么编译结果(包括中间结果)都将存放在build/src 目录(这个目录跟原有的src目录对应),指定 bin 目录后,相当于在编译时将src重命名为bin,所有的中间结果和目标二进制都将存放在bin目录。

换个地方保存二目标进制
不论是否指定编译输出目录,我们都可以通过SET指令重新定义EXECUTABLE_OUTPUT_PATH 和LIBRARY_OUTPUT_PATH 变量来指定最终的目标二进制的位置(指最终生成的hello或者最终的共享库,不包含编译生成的中间文件)
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
在第一节我们提到了< projectname>_BINARY_DIR和PROJECT_BINARY_DIR 变量,他们指的编译发生的当前目录,如果是内部编译,就相当于 PROJECT_SOURCE_DIR 也就是工程代码所在目录,如果是外部编译,指的是外部编译所在目录,也就是本例中的 build目录。
所以,上面两个指令分别定义了:
可执行二进制的输出路径为build/bin
库的输出路径为build/lib.
问题是,我应该把这两条指令写在工程的 CMakeLists.txt 还是 src 目录下的CMakeLists.txt,把握一个简单的原则,在哪里ADD_EXECUTABLE或ADD_LIBRARY,如果需要改变目标存放路径,就在哪里加入上述的定义。
在这个例子里,当然就是指src下的CMakeLists.txt 了

如何安装
首先先补上为添加的文件:

  • 添加doc目录及文件:
    mkdir doc
    vi doc/hello.txt

  • 在工程目录添加runhello.sh脚本,内容为:
    hello

  • 添加工程目录中的COPYRIGHT 和 README
    touch COPYRIGHT
    touch README

在这里插入图片描述

  • 安装 COPYRIGHT/README,直接修改主工程文件CMakelists.txt,加入以下指令:
    INSTALL(FILES COPYRIGHT README DESTINATION share/doc)
  • 安装 runhello.sh,直接修改主工程文件CMakeLists.txt,加入如下指令:
    INSTALL(PROGRAMS runhello.sh DESTINATION bin)
    3,安装 doc 中的hello.txt,这里有两种方式:一是通过在doc目录建立CMakeLists.txt 并将 doc 目录通过ADD_SUBDIRECTORY 加入工程来完成。另一种方法是直接在工程目录通过INSTALL(DIRECTORY 来完成),前者比较简单,各位可以根据兴趣自己完成,我们来尝试后者,顺便演示以下DIRECTORY 的安装。因为hello.txt 要安装到/< prefix>/share/doc/cmake/t2,所以我们不能直接安装整个doc目录,这里采用的方式是安装 doc ” 目录中的内容,也就是使用 doc/”在工程文件中添加
    INSTALL(DIRECTORY doc/ DESTINATION share/doc/)

重要:
这里在make前需要改变默认安装路径:DCMAKE_INSTALL_PREFIX
cmake -DCMAKE_INSTALL_PREFIX=/home/lbf/cn/usr . .
然后运行
make
make install
在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值