0voice-1.4.1-cmake

CMake介绍

  • 上一节讲了 Makefile , 它和 CMake 的关系?
  • CMake 就像一个高级的“项目配置工具”,你告诉它你的项目有哪些源文件、需要什么库、如何编译等。然后,CMake 会根据你的操作系统和你想使用的构建系统(比如 makeVisual \ Studio),自动为你生成对应的“施工图纸”(Makefile.sln/.vcxproj)。最后,你再使用这些“图纸”来实际“施工”(即运行 make 命令或在 Visual Studio 中构建项目)。

单个文件目录实现

  • CMake 在查找项目构建定义时,会默认且只查找名为 CMakeLists.txt 的文件 (注意文件尾部有个s) 。
  • 每个 CMake 项目的根目录都必须包含一个名为 CMakeLists.txt 的文件。这是 CMake 开始解析项目构建规则的入口点 。
1 # 单个目录实现
2 # CMake 最低版本号要求
3 cmake_minimum_required (VERSION 2.8)
4 # 工程,他不是执行文件名
5 PROJECT(0voice)
6 # 手动加入文件 ${变量名},比如${SRC_LIST}
7 SET(SRC_LIST main.c)
8 SET(SRC_LIST2 main2.c)
9 # MESSAGE和echo类似
10 MESSAGE(STATUS "THIS IS BINARY DIR " ${PROJECT_BINARY_DIR})
11 MESSAGE(STATUS "THIS IS SOURCE DIR " ${PROJECT_SOURCE_DIR})
12
13 # 生产执行文件名0voice 0voice2
14 ADD_EXECUTABLE(0voice ${SRC_LIST})
15 ADD_EXECUTABLE(0voice2 ${SRC_LIST2})
  • cmake 关键字的大小写没有影响。
  • SET(SRC_LIST main.c) 类似于 Makefile 中的 SRC_LIST = main.c,就是变量名赋值。
  • PROJECT_BINARY_DIR( 顶层运行 cmake 命令时的绝对路径 ) 、 PROJECT_SOURCE_DIR ( 顶层 CMakeLists.txt 的绝对路径 ) 。
  • 先创建一个 build 文件夹,在这文件夹下 cmake .. ( 原因是 CMakeLists 在上一级目录),这样子 cmake 产生的过程文件就在 build 下。 ( 最好每次手动创建一个 )。
  • 执行 cmake ..,最大的感受就是创建一个复杂的 Makefile 文件,包含了所有编译和链接规则、依赖关系、路径和变量。所有的细节都是 CMake 根据简洁的 CMakeLists.txt 文件( 只写了 add_executable(0voice main.c)add_executable(0voice2 main2.c) 这样的命令)自动推导并生成的。

拿上一个 Makefile 举例

.PHONY: main clean
simple: main.o foo.o
	@echo "simple: main.o foo.o"
	gcc -o simple main.o foo.o

main.o: main.c
	@echo "gcc -o main.o -c main.c"
	gcc -o main.o -c main.c

foo.o: foo.c
	@echo "gcc -o foo.o -c foo.c"
	gcc -o foo.o -c foo.c

clean:
	rm simple main.o foo.o

等价于 cmake 中的 add_executable(simple main.c foo.c),执行后会自动帮你生成。

  • 源代码更加复杂的依赖关系,则需要其他语法解决。
  • 简单的、单目录项目,在源文件下放一个 CMakeLists.txt 即可。
my_simple_project/
├── CMakeLists.txt
├── main.c
└── foo.c

单个文件目录实现 2

  • 目录结构
.
├── build/
├── CMakeLists.txt
├── doc/
│   ├── darren.txt
│   └── README.MD
└── src/
    ├── CMakeLists.txt
    └── main.c
  • 顶层 CMakeLists.txt
# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)
PROJECT(0VOICE)

MESSAGE(STATUS "0voice top PROJECT_BINARY_DIR " "${PROJECT_BINARY_DIR}")
MESSAGE(STATUS "0voice top PROJECT_SOURCE_DIR " "${PROJECT_SOURCE_DIR}")
# CMAKE_CURRENT_SOURCE_DIR它的CMakeLists.txt所在的当前源目
MESSAGE(STATUS "0voice top CMAKE_CURRENT_SOURCE_DIR " "${CMAKE_CURRENT_SOURCE_DIR}")

# 添加子目录
ADD_SUBDIRECTORY(src)

#INSTALL(FILES COPYRIGHT README DESTINATION share/doc/cmake/0voice)
# 安装doc到 share/doc/cmake/0voice目录
# 默认/usr/local/
# 指定自定义目录, 比如 cmake -DCMAKE_INSTALL_PREFIX=/tmp/usr ..
INSTALL(DIRECTORY doc/ DESTINATION share/doc/cmake/0voice)
  • src 中的 CMakeLists.txt
# 单个目录实现
# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)
# 工程
# PROJECT(0VOICE)
# 手动加入文件
SET(SRC_LIST main.c)
MESSAGE(STATUS "0voice sub PROJECT_BINARY_DIR" ${PROJECT_BINARY_DIR})
MESSAGE(STATUS "0voice sub PROJECT_SOURCE_DIR" ${PROJECT_SOURCE_DIR})
# CMAKE_CURRENT_SOURCE_DIR它的CMakeLists.txt所在的当前源目
MESSAGE(STATUS "0voice sub CMAKE_CURRENT_SOURCE_DIR " ${CMAKE_CURRENT_SOURCE_DIR})

ADD_EXECUTABLE(youzi ${SRC_LIST})
# 演示
ADD_EXECUTABLE(youzi2 ${SRC_LIST})
# 将执行文件安装到bin目录
# 默认/usr/local/
#指定自定义目录,比如 cmake -DCMAKE_INSTALL_PREFIX=/tmp/usr ..
INSTALL(TARGETS youzi RUNTIME DESTINATION bin)
INSTALL(TARGETS youzi2 RUNTIME DESTINATION bin)
  • ADD_SUBDIRECTORY(src)CMake 会进入 src 目录,读取并执行 src/CMakeLists.txt 中的命令。这使得项目可以模块化,将不同部分的构建逻辑放在各自的子目录中。
  • CMAKE_CURRENT_SOURCE_DIR : CMake 命名执行的当前目录。
  • INSTALL(DIRECTORY doc/ DESTINATION share/doc/cmake/0voice):
    • 针对文件本身。
    • DIRECTORY 关键字表示你要安装一个目录。
    • DESTINATION 关键字指定了安装的目标路径。
  • INSTALL(TARGETS youzi2 RUNTIME DESTINATION bin):
    • TARGETS 关键字表示你要安装一个或多个 CMake 目标。
    • youzi2 是你要安装的目标名称。这个目标必须在之前的 CMakeLists.txt 中通过 add_executable(youzi2 ...)add_library(youzi2 ...) 定义过。
    • RUNTIME: 关键字特指可执行文件。虽然 youzi2 本来就是可执行文件,但这里也要说明,因为 target 也能指定其他类型的文件。

多个目录实现 - 子目录编译成库文件

  • 文件结构
.
├── CMakeLists.txt          # 根目录的 CMakeLists.txt
├── doc                     # 文档目录
│   ├── darren.txt
│   └── README.MD
└── src                     # 源代码主目录
    ├── CMakeLists.txt      # src 目录的 CMakeLists.txt
    ├── dir1                # 子模块1目录
    │   ├── CMakeLists.txt  # dir1 目录的 CMakeLists.txt
    │   ├── dir1.c
    │   └── dir1.h
    ├── dir2                # 子模块2目录
    │   ├── CMakeLists.txt  # dir2 目录的 CMakeLists.txt
    │   ├── dir2.c
    │   └── dir2.h
    └── main.c              # 主程序的源文件

  1. INCLUDE_DIRECTORIES

    当项目变得复杂的时候,并且一个源文件需要包含位于其自身目录之外的自定义头文件时,需要用到 INCLUDE_DIRECTORIES

    语法:INCLUDE_DIRECTORIES("xxx")

  2. ADD_LIBRARY

    ADD_LIBRARY( hello_shared SHARED libHelloSLAM.cpp ) 生成动态库
    ADD_LIBRARY( hello_shared STATIC libHelloSLAM.cpp ) 生成静态库
    
    • 对于库文件的理解:一个库(无论是静态库 .a/.lib 还是动态库 .so/.dll)的本质是编译好的二进制代码,这些代码包含了函数的定义和变量的定义。.cpp 完成对函数和变量的定义,生成目标文件.o, 链接器会把 .o 打包成库 。
    • 静态库的标准命名约定是 lib 前缀,.a 后缀。动态库的标准命名约定是 lib 前缀,.so 后缀 (lib + 库名 + .a\ .so , 前后缀自动生成)。
    • 静态库:链接器将库中的机器码复制到应用程序的可执行文件中,运行时就独立。
    • 动态库:链接器在应用程序可执行文件中只写入对动态库中函数的引用(符号信息),而不是实际机器码,运行时,操作系统加载器找到并加载动态库文件到内存,然后将应用程序中的引用解析为内存中动态库里实际机器码的地址
    • hello_shared 是库的名字 , SHAREDSTATIC 区分动\静态
  3. TARGET_LINK_LIBRARIES

    • 链接库到执行文件上
    TARGET_LINK_LIBRARIES(darren dir1 dir2)
    
  4. build

    • build 目录下生成一个与源文件目录结构平行的目录结构,用于存放所有生成的构建文件 。
    • 在顶层目录 build 使用 make , 是能够调用其下子文件夹下的 Makefile 的 。
  • make 指令尝试构建 Makefile 中定义的默认目标, install 指令在 CMakeLists.txt 中定义的是一个独立的构建目标。CMake 会在生成的 Makefile 中创建一个名为 install 的目标。也就是说 make 启动不了 CMakeLists.txt 里面的安装指令。
posted @ 2025-09-14 17:52  xqy2003  阅读(6)  评论(0)    收藏  举报