0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

CMake实战教程-3

汽车电子技术 来源:物联网IoT开发 作者:杰杰mcu 2023-02-28 16:22 次阅读

前言

CMake实战教程(一)

CMake实战教程(二)

从本小节开始,后面所有的构建我们都将采用 out-of-source build 外部构建的方式去编写构建工程代码,构建目录是工程目录下的 build 目录。

从上一篇文章我们就知道,通过aux_source_directory命令可以扫描某个目录下的所有源码,但是更深一层的目录源码就找不到了,因此不满足我们真正工程项目的需要,毕竟真的工程会有很多个目录的,这些目录在的地方还不一样,现在就来解决这个问题…

定个小目标

  • CMake自动构建多个源码目录下的工程

实现

首先,起码工程中得有多个目录对吧,按照编程习惯,我喜欢把源码放到 src 目录下,把头文件放到 inc 目录下,那么把上一篇文章的代码分离放开,具体如下:

jie@pc:~/github/cmake/section4$ tree
.
├── build.sh
├── CMakeLists.txt
├── inc
│   └── power.h
├── main.c
└── power
    ├── CMakeLists.txt
    └── power.c

2 directories, 6 files

很显然,从上面的目录结构中可以看出,有两个CMakeLists.txt文件,一个是在根目录下,一个是在src目录下:

./CMakeLists.txt
./src/CMakeLists.txt

其实无论在何处,CMakeLists.txt文件都可以被 CMake 命令识别并且自动构建,但是,我们自动构建的时候,都会有一个顶层目录CMakeLists.txt文件,它就是cmake的入口文件,而在顶层目录下,如果某些子目录中存在CMakeLists.txt文件,那么 CMake 可以将顶层的环境变量传递到子目录下,就好比,你在某个文件中定义了个全局变量,你在那个文件中的子函数都可以使用这个全局变量,有点类似的道理,说白了就是作用域。。。

添加子目录

那么为了能让 CMake 去识别到src目录下的./src/CMakeLists.txt文件,我们可以在顶层./CMakeLists.txt文件添加一个子目录,用以下这个命令:

add_subdirectory(source_dir [binary_dir]
                [EXCLUDE_FROM_ALL])

add_subdirectory 这条命令的作用是为构建添加一个子路径。

  • source_dir 选项指定了 CMakeLists.txt 源文件和代码文件的位置。如果 source_dir 是一个相对路径,那么 source_dir 选项会被解释为相对于当前的目录,不过它也可以是一个绝对路径。在 source_dir 指定路径下的 CMakeLists.txt 将会在当前输入文件的处理过程执行到该命令之前,立即被 CMake 处理。
  • binary_dir 选项指定了输出文件的路径,同样的, binary_dir 可以相对路径,也可以是一个绝对路径。不过杰杰目前暂未使用到binary_dir,感兴趣的兄弟姐妹可以自己去了解它。
  • 如果指定了 EXCLUDE_FROM_ALL 选项,在子路径下的目标默认不会被包含到父路径的 ALL 目标里,并且也会被排除在工程文件之外,用户必须显式构建在子路径下的目标。

添加路径

include_directories([AFTER|BEFORE] [SYSTEM] dir1 dir2 ...)

同理可以使用include_directories命令将给定的路径添加到编译器搜索包含文件(.h 文件)的路径列表中,这是为了让编译器找到合适的头文件。如果指定了 SYSTEM 选项,编译器将会认为该路径是某种平台上的系统包含路径。

遍历

在cmake中,很多变量都是以列表的形式存在,比如上面的两个命令,添加头文件与添加子目录,我们可以用列表的形式添加进去,比如include_directories(dir1 dir2 dir3),那么这些dir,那么cmake也提供了遍历这些列表的方法,这与C语言中的for循环都差不多,可以使用以下命令去遍历它:

foreach(loop_var arg1 arg2 ...)
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
...
endforeach(loop_var)

注意,这个命令需要配合 endforeach 命令使用,endforeach 表示结束遍历

所有的 foreach 和与之匹配的 endforeach 命令之间的命令,会根据list的每个变量都执行一遍,在每次迭代中,循环变量${loop_var}将会被设置为 list 中的当前变量值。

为了演示,我就将foreach命令去变量指定的INCDIRS(虽然只有一个变量,不过不影响演示),把INCDIRS列表的内容一个个添加到路径中:

### 头文件
set(INCDIRS "inc")

# 添加头文件目录
foreach(incdir ${INCDIRS})
    include_directories(${incdir})
endforeach()

当然,我在这提到遍历并不是因为必须要使用这个命令,而是为后续的文章做铺垫。

构建库

如果一个工程中有非常多的源码,那么总不能将所有的源码都使用add_executable命令去生成可执行文件是不是,我们可以让这个可执行文件去依赖库,静态库,动态库都可以,那么在这里就简单说说add_executable命令依赖静态库生成可执行文件,那么问题来了,静态库从哪来?

很显然,静态库是我们的一些源码文件生成的,在生成静态库后,再通过静态库去构建可执行文件,那么就用到了CMake中的add_library命令:

add_library( [STATIC | SHARED | MODULE]
            [EXCLUDE_FROM_ALL]
            source1 source2 ... sourceN)

它的作用就是使用指定的源文件向工程中添加一个库。添加一个名为的库文件,在一个工程的全局域内必须是唯一的,这个库文件将会根据命令里列出的源文件(source1 source2 ... sourceN)来创建。待构建的库文件的实际文件名根据对应平台的命名约定来构造(比如 可以是lib.a 或者.lib)。通过指定STATICSHARED,或者 MODULE 参数用来指定要创建的库的类型。

  • STATIC 库是目标文件的归档文件,在链接其它目标的时候使用。
  • SHARED 库会被动态链接,在运行时被加载。
  • MODULE 库是不会被链接到其它目标中的插件,但是可能会在运行时使用 dlopen-系列的函数动态链接。如果没有类型被显式指定,这个选项将会根据变量 BUILD_SHARED_LIBS 的当前值是否为真决定是 STATIC 还是 SHARED
# 查找当前目录下的所有源文件
# 并将名称保存到 DIR_LIB_SRCS 变量
file(GLOB_RECURSE DIR_LIB_SRCS *.c)
file(GLOB_RECURSE DIR_LIB_HDRS *.h)

### 生成链接库
add_library(${LIB_NAME} ${DIR_LIB_SRCS} ${DIR_LIB_HDRS})

当然,为了我们的CMakeLists.txt文件有更好的兼容性,也不必每次都去写对应的库文件名字,杰杰自己会使用正则表达式去解析当前目录的名字,并以这个目录名字去作为库的命名,正则表达式如下:

# 正则表达式得到当前目录名字作为提供给上一层的库名字
string(REGEX REPLACE ".*/(.*)" "\\\\1" LIB_NAME ${CMAKE_CURRENT_SOURCE_DIR})

工程demo

顶层./CMakeLists.txt文件内容:

# CMake 最低版本号要求
cmake_minimum_required(VERSION 2.8)

# 项目信息
project(targets)

# 设置可执行文件目标
set(TARGETS "targets")

# 子目录
set(SUBDIRS "power")

# 头文件
set(INCDIRS "inc")

# 指定编译器
set(CMAKE_C_COMPILER "gcc")

#判断编译器类型,如果是gcc编译器,则在编译选项中加入c++11支持
if(CMAKE_COMPILER_IS_GNUCXX)
    set(CMAKE_CXX_FLAGS "-std=c++11 ${CMAKE_CXX_FLAGS}")
endif(CMAKE_COMPILER_IS_GNUCXX)

#指定编译类型
SET(CMAKE_BUILE_TYPE "RELEASE")

# 查找当前目录下的所有源文件
# 并将名称保存到 DIR_SRCS 变量
aux_source_directory(. DIR_SRCS)

# 指定生成目标
add_executable(${TARGETS} ${DIR_SRCS})

# 添加头文件目录
foreach(incdir ${INCDIRS})
    include_directories(${incdir})
endforeach()


# 添加子目录
foreach(subdir ${SUBDIRS})
    add_subdirectory(${subdir})
endforeach()

# 添加链接库
target_link_libraries(${TARGETS} ${SUBDIRS})

src目录下的./src/CMakeLists.txt文件内容:

# 查找当前目录下的所有源文件
# 并将名称保存到 DIR_LIB_SRCS 变量
file(GLOB_RECURSE DIR_LIB_SRCS *.c)
file(GLOB_RECURSE DIR_LIB_HDRS *.h)

# 正则表达式得到当前目录名字作为提供给上一层的库名字
string(REGEX REPLACE ".*/(.*)" "\\\\1" LIB_NAME ${CMAKE_CURRENT_SOURCE_DIR}) 

# 生成链接库
add_library(${LIB_NAME} ${DIR_LIB_SRCS} ${DIR_LIB_HDRS})

最后直接运行./build去构建工程与编译工程即可

代码下载

https://github.com/jiejieTop/cmake

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 目录
    +关注

    关注

    3

    文章

    13

    浏览量

    8176
  • Build
    +关注

    关注

    0

    文章

    26

    浏览量

    11979
  • 构建
    +关注

    关注

    0

    文章

    9

    浏览量

    7228
收藏 人收藏

    评论

    相关推荐

    cmake是什么?cmake的特性和编译原理(cmake原理和cmake编译过程)

    CMake是一个开源、跨平台的工具系列,是用来构建、测试和打包软件。
    的头像 发表于 07-18 10:53 1868次阅读
    <b class='flag-5'>cmake</b>是什么?<b class='flag-5'>cmake</b>的特性和编译原理(<b class='flag-5'>cmake</b>原理和<b class='flag-5'>cmake</b>编译过程)

    3. CMake 中 set 的使用 - 上___CMake 保姆级教程【C_C++】

    CMake
    jf_97106930
    发布于 :2023年05月18日 15:39:48

    CMake 入门实战

    CMake 入门实战,,
    发表于 09-28 12:38

    Cmake学习的总结(二)

    大家好,上次给大家分享了第一篇 cmake 文章:cmake学习总结(一),今天继续给大家分享cmake。那么废话就不多说,开始内容分享。
    的头像 发表于 12-24 17:54 451次阅读

    如何使用CMake工具套件构建CUDA应用程序

    我希望这篇文章向您展示了 CMake 如何自然地支持构建 CUDA 应用程序。如果您是 CMake 的现有用户,请试用 CMake 3 . 9 并利用改进的 CUDA 支持。如果您不是 CMa
    的头像 发表于 04-01 17:42 3873次阅读
    如何使用<b class='flag-5'>CMake</b>工具套件构建CUDA应用程序

    RT-Thread V4.1.0新特性CMake介绍与构建CMake工程

        CMake 支持 在 RT-Thread 4.1.0 正式发布版中,添加了对 CMake 的支持。开发者可以使用 SCons 工具来生成 CMakeLists.txt 文件。 为何要支持
    的头像 发表于 05-24 19:20 2603次阅读

    RT-Thread 4.1.0的CMake构建教程

    CMake 支持 在 RT-Thread 4.1.0 正式发布版中,添加了对 CMake 的支持。开发者可以使用 SCons 工具来生成 CMakeLists.txt 文件。 为何要支持 CMake
    的头像 发表于 05-25 11:06 2744次阅读

    CMake用法详解

    CMake用法详解
    发表于 10-25 16:28 2次下载

    CMake实战教程-1

    CMake 是一个跨平台的构建系统生成工具。它使用平台无关的 CMake 清单文件CMakeLists.txt,指定工程的构建过程;源码树的每个路径下都有这个文件。CMake 产生一个适用于具体平台的构建系统,用户使用这个系统构
    的头像 发表于 02-14 10:42 480次阅读
    <b class='flag-5'>CMake</b>的<b class='flag-5'>实战</b>教程-1

    CMake实战教程-2

    第一个问题,在运行`cmake .`后会产生很多垃圾文件,那么我们可以让它在一个build目录下去编译,生成的垃圾文件放在这个目录下就好了,不需要的时候直接清除即可。
    的头像 发表于 02-14 10:43 457次阅读

    CMake实战教程-3

    从本小节开始,后面所有的构建我们都将采用 `out-of-source build` 外部构建的方式去编写构建工程代码,构建目录是工程目录下的 `build` 目录。
    的头像 发表于 02-14 10:45 403次阅读

    CMake实战教程-1

    在学习`CMake`之前,一直都是自己在手敲`Makefile`文件,当项目的文件一旦多了起来,自己手动写`Makefile`就不是那么好玩了,也曾经了解了一下`autotools`,但是seeed的柱哥说有`CMake`这个东西,所以我就去学习了,也在网上搜过对比,下面
    的头像 发表于 02-28 16:17 523次阅读
    <b class='flag-5'>CMake</b><b class='flag-5'>实战</b>教程-1

    CMake实战教程-2

    第一个问题,在运行`cmake .`后会产生很多垃圾文件,那么我们可以让它在一个build目录下去编译,生成的垃圾文件放在这个目录下就好了,不需要的时候直接清除即可。
    的头像 发表于 02-28 16:18 456次阅读

    在Linux下如何使用CMake编译程序

    CMake是开源、跨平台的构建工具,可以让我们通过编写简单的配置文件去生成本地的Makefile,这个配置文件是独立于运行平台和编译器的,这样就不用亲自去编写Makefile了,而且配置文件可以直接
    的头像 发表于 11-08 16:15 1701次阅读
    在Linux下如何使用<b class='flag-5'>CMake</b>编译程序

    CMake构建后的项目结构解析

    一、 CMake构建后的项目结构解析(Analysis of the Project Structure After CMake Build) 1.1 CMake构建后的目录结构(Directory
    的头像 发表于 11-10 10:27 406次阅读
    <b class='flag-5'>CMake</b>构建后的项目结构解析