1.CMake编译原理

CMake是一种跨平台编译工具,比make更为高级,使用起来要方便得多。CMake主要是编写CMakeLists.txt文件,然后用cmake命令将CMakeLists.txt文件转化为make所需要的makefile文件,最后用make命令编译源码生成可执行程序或共享库(so(shared object))。因此CMake的编译基本就两个步骤:

cmake
make
cmake 指向CMakeLists.txt所在的目录,例如cmake … 表示CMakeLists.txt在当前目录的上一级目录。cmake后会生成很多编译的中间文件以及makefile文件,所以一般建议新建一个新的目录,专门用来编译,例如

mkdir build
cd build
cmake …
make

make根据生成makefile文件,编译程序。

2.使用Cmake编译程序

我们编写一个关于开平方的C/C++程序项目,即b= sqrt(a),以此理解整个CMake编译的过程。

a.准备程序文件

文件目录结构如下:

.
├── build
├── CMakeLists.txt
├── include
│ └── b.h
└── src
├── b.c
└── main.c

头文件b.h,如下所示:

#ifndef B_FILE_HEADER_INC
#define B_FIEL_HEADER_INC

#include<math.h>

double cal_sqrt(double value);

#endif

头文件b.c,如下所示:

#include “…/include/b.h”

double cal_sqrt(double value)
{
return sqrt(value);
}

main.c主函数,如下所示:

#include “…/include/b.h”
#include <stdio.h>
int main(int argc, char** argv)
{
double a = 49.0;
double b = 0.0;

printf(“input a:%f\n”,a);
b = cal_sqrt(a);
printf(“sqrt result:%f\n”,b);
return 0;
1
2
3
4
}

b.编写CMakeLists.txt

接下来编写CMakeLists.txt文件,该文件放在和src,include的同级目录,实际方哪里都可以,只要里面编写的路径能够正确指向就好了。CMakeLists.txt文件,如下所示:

#1.cmake verson,指定cmake版本
cmake_minimum_required(VERSION 3.2)

#2.project name,指定项目的名称,一般和项目的文件夹名称对应
PROJECT(test_sqrt)

#3.head file path,头文件目录
INCLUDE_DIRECTORIES(
include
)

#4.source directory,源文件目录
AUX_SOURCE_DIRECTORY(src DIR_SRCS)

#5.set environment variable,设置环境变量,编译用到的源文件全部都要放到这里,否则编译能够通过,但是执行的时候会出现各种问题,比如"symbol lookup error xxxxx , undefined symbol"
SET(TEST_MATH
${DIR_SRCS}
)

#6.add executable file,添加要编译的可执行文件
ADD_EXECUTABLE(${PROJECT_NAME} ${TEST_MATH})

#7.add link library,添加可执行文件所需要的库,比如我们用到了libm.so(命名规则:lib+name+.so),就添加该库的名称
TARGET_LINK_LIBRARIES(${PROJECT_NAME} m)

CMakeLists.txt主要包含以上的7个步骤,具体的意义,请阅读相应的注释。

c.编译和运行程序

准备好了以上的所有材料,接下来,就可以编译了,由于编译中出现许多中间的文件,因此最好新建一个独立的目录build,在该目录下进行编译,编译步骤如下所示:

mkdir build
cd build
cmake …
make

操作后,在build下生成的目录结构如下:

├── build
│ ├── CMakeCache.txt
│ ├── CMakeFiles
│ │ ├── 3.2.2
│ │ │ ├── CMakeCCompiler.cmake
│ │ │ ├── CMakeCXXCompiler.cmake
│ │ │ ├── CMakeDetermineCompilerABI_C.bin
│ │ │ ├── CMakeDetermineCompilerABI_CXX.bin
│ │ │ ├── CMakeSystem.cmake
│ │ │ ├── CompilerIdC
│ │ │ │ ├── a.out
│ │ │ │ └── CMakeCCompilerId.c
│ │ │ └── CompilerIdCXX
│ │ │ ├── a.out
│ │ │ └── CMakeCXXCompilerId.cpp
│ │ ├── cmake.check_cache
│ │ ├── CMakeDirectoryInformation.cmake
│ │ ├── CMakeOutput.log
│ │ ├── CMakeTmp
│ │ ├── feature_tests.bin
│ │ ├── feature_tests.c
│ │ ├── feature_tests.cxx
│ │ ├── Makefile2
│ │ ├── Makefile.cmake
│ │ ├── progress.marks
│ │ ├── TargetDirectories.txt
│ │ └── test_sqrt.dir
│ │ ├── build.make
│ │ ├── C.includecache
│ │ ├── cmake_clean.cmake
│ │ ├── DependInfo.cmake
│ │ ├── depend.internal
│ │ ├── depend.make
│ │ ├── flags.make
│ │ ├── link.txt
│ │ ├── progress.make
│ │ └── src
│ │ ├── b.c.o
│ │ └── main.c.o
│ ├── cmake_install.cmake
│ ├── Makefile
│ └── test_sqrt
├── CMakeLists.txt
├── include
│ └── b.h
└── src
├── b.c
└── main.c

注意在build的目录下生成了一个可执行的文件test_sqrt,运行获取结果如下:

命令:
./test_sqrt
结果:
input a:49.000000
sqrt result:7.000000

CMakeLists.txt的基本结构

编写CMakeLists.txt最常用的功能就是调用其他的.h头文件和.so/.a库文件,将.cpp/.c/.cc文件编译成可执行文件或者新的库文件。

命令的官方网站:CMake Reference Documentation

最常用的命令如下(仅供后期查询,初期不需要细看):

本CMakeLists.txt的project名称

会自动创建两个变量,PROJECT_SOURCE_DIR和PROJECT_NAME

${PROJECT_SOURCE_DIR}:本CMakeLists.txt所在的文件夹路径

${PROJECT_NAME}:本CMakeLists.txt的project名称

project(xxx)

获取路径下所有的.cpp/.c/.cc文件,并赋值给变量中

aux_source_directory(路径 变量)

给文件名/路径名或其他字符串起别名,用${变量}获取变量内容

set(变量 文件名/路径/…)

添加编译选项

add_definitions(编译选项)

打印消息

message(消息)

编译子文件夹的CMakeLists.txt

add_subdirectory(子文件夹名称)

将.cpp/.c/.cc文件生成.a静态库

注意,库文件名称通常为libxxx.so,在这里只要写xxx即可

add_library(库文件名称 STATIC 文件)

将.cpp/.c/.cc文件生成可执行文件

add_executable(可执行文件名称 文件)

规定.h头文件路径

include_directories(路径)

规定.so/.a库文件路径

link_directories(路径)

对add_library或add_executable生成的文件进行链接操作

注意,库文件名称通常为libxxx.so,在这里只要写xxx即可

target_link_libraries(库文件名称/可执行文件名称 链接的库文件名称)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
通常一个CMakeLists.txt需按照下面的流程:

project(xxx) #必须

add_subdirectory(子文件夹名称) #父目录必须,子目录不必

add_library(库文件名称 STATIC 文件) #通常子目录(二选一)
add_executable(可执行文件名称 文件) #通常父目录(二选一)

include_directories(路径) #必须
link_directories(路径) #必须

target_link_libraries(库文件名称/可执行文件名称 链接的库文件名称) #必须
1
2
3
4
5
6
7
8
9
10
11
除了这些之外,就是些set变量的语句,if判断的语句,或者其他编译选项的语句,但基本结构都是这样的。

实例

我以自己曾经写的一段实际代码为例,来讲解究竟该怎么写CMakeLists。

实例地址:

码云:https://gitee.com/yngzMiao/protobuf-parser-tool

GitHub:https://github.com/yngzMiao/protobuf-parser-tool

实例的功能是生成和解析proto文件,分为C++和python版本。其中,C++版本就是采用CMakeLists.txt编写的,目录结构如下:

|—example_person.cpp
|—proto_pb2
|–Person.pb.cc
|–Person.pb.h
|—proto_buf
|—General_buf_read.h
|—General_buf_write.h
|—protobuf
|—bin
|—…
|—include
|—…
|—lib
|—…
1
2
3
4
5
6
7
8
9
10
11
12
13
14
目录结构含义:

protobuf:Google提供的相关解析库和头文件,被proto_pb2文件夹内引用;
proto_pb2:封装的Person结构和Person相关的处理函数,被proto_buf文件夹内引用;
proto_buf:封装的read和write函数,被example_persom.cpp文件引用。
也就是说:

example_person.cpp–>proto_buf文件夹–>proto_pb2文件夹–>protobuf文件夹

步骤

CMakeLists.txt的创建

在需要进行编译的文件夹内编写CMakeLists.txt,即含有.cpp/.c/.cc的文件夹内:

即目录结构如下:

|—example_person.cpp
|—CMakeLists.txt
|—proto_pb2
|–Person.pb.cc
|–Person.pb.h
|–CMakeLists.txt
|—proto_buf
|—General_buf_read.h
|—General_buf_write.h
|—protobuf
|—bin
|—…
|—include
|—…
|—lib
|—…
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
CMakeLists.txt的编写

本项目的CMakeLists.txt的文件数量是2个,目录层次结构为上下层关系。通常的解决方案,就是将下层目录编译成一个静态库文件,让上层目录直接读取和调用,而上层目录就直接生成一个可执行文件。

上层CMakeLists.txt的内容为:

cmake_minimum_required(VERSION 3.0)
project(example_person)

如果代码需要支持C++11,就直接加上这句

SET(CMAKE_CXX_FLAGS “${CMAKE_CXX_FLAGS} -std=c++0x”)

如果想要生成的可执行文件拥有符号表,可以gdb调试,就直接加上这句

add_definitions("-Wall -g")

设置变量,下面的代码都可以用到

set(GOOGLE_PROTOBUF_DIR ${PROJECT_SOURCE_DIR}/protobuf)
set(PROTO_PB_DIR ${PROJECT_SOURCE_DIR}/proto_pb2)
set(PROTO_BUF_DIR ${PROJECT_SOURCE_DIR}/proto_buf)

编译子文件夹的CMakeLists.txt

add_subdirectory(proto_pb2)

规定.h头文件路径

include_directories(${PROJECT_SOURCE_DIR}
${PROTO_PB_DIR} ${PROTO_BUF_DIR}
)

生成可执行文件

add_executable(${PROJECT_NAME} example_person.cpp )

链接操作

target_link_libraries(${PROJECT_NAME}
general_pb2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
如果是初学者,这一段可能看不到两个地方,第一是链接操作的general_pb2,第二是按照上文的CMakeLists.txt的流程,并没有规定link_directories的库文件地址啊,这是什么道理?

这两个其实是一个道理,add_subdirectory起到的作用!

当运行到add_subdirectory这一句时,会先将子文件夹进行编译,而libgeneral_pb2.a是在子文件夹中生成出来的库文件。子文件夹运行完后,父文件夹就已经知道了libgeneral_pb2.a这个库,因而不需要link_directories了。

同时,另一方面,在add_subdirector之前set的各个变量,在子文件夹中是可以调用的!

下层CMakeLists.txt的内容为:

project(general_pb2)

aux_source_directory(${PROJECT_SOURCE_DIR} PB_FILES)

add_library(${PROJECT_NAME} STATIC ${PB_FILES})

include_directories(${PROJECT_SOURCE_DIR}
${GOOGLE_PROTOBUF_DIR}/include
)

link_directories(${GOOGLE_PROTOBUF_DIR}/lib/)

target_link_libraries(PROJECTNAMEprotobuf)123456789101112131415在这里,GOOGLEPROTOBUFDIR是上层CMakeLists.txt中定义的,libprotobuf.a是在{PROJECT_NAME} protobuf ) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 在这里,GOOGLE_PROTOBUF_DIR是上层CMakeLists.txt中定义的,libprotobuf.a是在PROJECTN​AMEprotobuf)123456789101112131415在这里,GOOGLEP​ROTOBUFD​IR是上层CMakeLists.txt中定义的,libprotobuf.a是在{GOOGLE_PROTOBUF_DIR}/lib/目录下的。

显然可见,这就是一个标准的CMakeLixts.txt的流程。

CMakeLists.txt的编译

一般CMakeLists.txt是,在最顶层创建build文件夹,然后编译。即:

mkdir build && cd build
cmake …
make
1
2
3
最终生成可执行文件example_person。

可以通过以下命令来运行该可执行文件:

./example_person
1

————————————————
版权声明:本文为CSDN博主「Yngz_Miao」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_38410730/article/details/102477162

cmakelist.txt 编写教程相关推荐

  1. CMake 使用方法 CMakeList.txt编写简单分析

    cmake 简介 CMake是一个跨平台的安装(编译)工具,可以用简单的语句来描述所有平台的安装(编译过程).他能够输出各种各样的makefile或者project文件,能测试编译器所支持的C++特性 ...

  2. CmakeList.txt 中添加某个子目录下的所有源文件方法

    假设有目录 A,在目录A下面有子目录 A1 和 CmakeList.txt.在子目录 A1 中有 a.c b.c c.c .... z.c 等N个源文件.因为 A1 目录下源文件较多,手动去添加比较麻 ...

  3. CMakeList.txt的简单使用

    CMakeList.txt的简单使用 CMake 简介 CMake 是一个跨平台的自动化建构系统,它使用一个名为 CMakeLists.txt 的文件来描述构建过程,可以产生标准的构建文件,如 Uni ...

  4. 关于ROS功能包里package.xml和CMakeList.txt的源码分析

    catkin简介 catkin简介 packagexml 格式1 格式2 CMakeListtxt meta package 典型ROS应用添加自定义message文件 修改packagexml 修改 ...

  5. 一个很不错的bash脚本编写教程

    一个很不错的bash脚本编写教程 建立一个脚本 Linux中有好多中不同的shell,但是通常我们使用bash (bourne again shell) 进行shell编程,因为bash是免费的并且很 ...

  6. ROS中使用思岚激光雷达进行跟随和Cmakelist.txt讲解

    之前一直在弄ROS小车的底盘和编写底盘的代码,现在把底盘已经弄好了,所以开始编写一些上层的代码和算法,这篇博客主要介绍使用思岚雷达来进行跟随和Cmakelist.txt的讲解,本人注重于手动DIY R ...

  7. linux编程 —— vscode 开发编译 CMakeList.txt 学习笔记

    文档声明: 以下资料均属于本人在学习过程中产出的学习笔记,如果错误或者遗漏之处,请多多指正.并且该文档在后期会随着学习的深入不断补充完善.感谢各位的参考查看. 笔记资料仅供学习交流使用,转载请标明出处 ...

  8. 【Android 高性能音频】hello-oboe 示例解析 ( Oboe 源代码依赖 | CMakeList.txt 构建脚本分析 | Oboe 源代码构建脚本分析 )

    文章目录 一.Oboe 源码路径 二.阅读 CMakeList.txt 查看依赖 三.hello-oboe 中 NDK 的 CMakeList.txt 构建脚本 四.Oboe 源码 的 CMakeLi ...

  9. 【Android RTMP】音频数据采集编码 ( FAAC 头文件与静态库拷贝到 AS | CMakeList.txt 配置 FAAC | AudioRecord 音频采样 PCM 格式 )

    文章目录 安卓直播推流专栏博客总结 一. FAAC 头文件与静态库拷贝到 Android Studio 二. CMakeList.txt 构建脚本配置 三. Java 层 AudioRecord 音频 ...

最新文章

  1. 公司终于决定放弃微服务传统设计模式,全面拥抱 DDD!
  2. Java并发编程(十四)并发容器类
  3. vue 声明周期函数_Vue_生命周期函数
  4. 省赛热身赛之Median
  5. 最近做了一个安装包的安装流程图
  6. # 管道已结束_县城这条路启用自来水新管道,看看是否在你家附近...
  7. CSS清除默认样式,看完这篇彻底明白了
  8. JVM基础系列开篇:为什么要学虚拟机?
  9. stm32按键矩阵代码_STM32学习日志——电容触摸按键实验(20-06-27)
  10. java程序设计颜志军_毕业论文(设计)大学生竞赛管理系统的设计与实现.doc
  11. 微信小程序各种弹窗操作
  12. 突发奇想!自动化测试在测试过程中该怎样提升效率?
  13. 关于ASO优化刷榜、冲榜、维榜、锁榜科普百科
  14. 《如何阅读一本书》思维导图
  15. python中assert是什么意思_你常常看到 Python 代码中的 assert 是个啥?
  16. 酒水知识(六大基酒之金酒_Gin)
  17. ng-alain之G2图表
  18. 做自媒体如何快速实现财务自由
  19. 基于微信选修课报名小程序系统设计与实现 开题报告
  20. python基础练习之打飞机

热门文章

  1. 7种方式实现斐波那契数列
  2. 45页新能源充电桩运营平台规划与建设方案
  3. 微信公众号,文本消息带a标签的处理
  4. cookie实现记住密码
  5. 银行需要发行那么多种卡吗?
  6. DZ修改标题字数限制:Discuz X2.0 如何修改帖子标题字数限制
  7. 商标19类明细计算机,第19类商标经营范围及内容明细
  8. 程序设计入门——C语言【课堂练习】
  9. 一线互联网互联网架构师自述:GitHub标星10w+,ffmpeg音视频开发实战6陈超
  10. Ubuntu 12.10 安装ATI 显卡驱动