0voice-1.4.1-cmake
CMake介绍
- 上一节讲了
Makefile
, 它和CMake
的关系? CMake
就像一个高级的“项目配置工具”,你告诉它你的项目有哪些源文件、需要什么库、如何编译等。然后,CMake
会根据你的操作系统和你想使用的构建系统(比如make
或Visual \ 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 # 主程序的源文件
-
INCLUDE_DIRECTORIES
当项目变得复杂的时候,并且一个源文件需要包含位于其自身目录之外的自定义头文件时,需要用到
INCLUDE_DIRECTORIES
。语法:
INCLUDE_DIRECTORIES("xxx")
-
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
是库的名字 ,SHARED
和STATIC
区分动\静态
- 对于库文件的理解:一个库(无论是静态库
-
TARGET_LINK_LIBRARIES
- 链接库到执行文件上
TARGET_LINK_LIBRARIES(darren dir1 dir2)
-
build
- 在
build
目录下生成一个与源文件目录结构平行的目录结构,用于存放所有生成的构建文件 。 - 在顶层目录
build
使用make
, 是能够调用其下子文件夹下的Makefile
的 。
- 在
make
指令尝试构建Makefile
中定义的默认目标,install
指令在CMakeLists.txt
中定义的是一个独立的构建目标。CMake
会在生成的Makefile
中创建一个名为install
的目标。也就是说make
启动不了CMakeLists.txt
里面的安装指令。