400
社区成员
![](https://csdnimg.cn/release/cmsfe/public/img/topic.427195d5.png)
![](https://csdnimg.cn/release/cmsfe/public/img/me.40a70ab0.png)
![](https://csdnimg.cn/release/cmsfe/public/img/task.87b52881.png)
![](https://csdnimg.cn/release/cmsfe/public/img/share-circle.3e0b7822.png)
三要素:目标,依赖,命令
=:普通赋值,用于设置变量。
:=:立即展开赋值,表示变量的值在赋值时立即被计算。
?=:仅当变量未定义时才赋值。
+=:在变量的现有值后追加内容。
CC = gcc
CFLAGS = -Wall
SRC = main.c utils.c
OBJ = $(SRC:.c=.o)
target: dependencies
command
target:目标文件,通常是你想要生成的文件(如 .o 或可执行文件)。myprogram: main.o utils.o
gcc -o myprogram main.o utils.o
%.o: %.c
gcc -c $< -o $@
没有实际文件名
的目标,通常用于清理或强制执行某些任务。all: myprogram
clean:
rm -f *.o myprogram
#### 5. 条件判断
Makefile 支持条件判断,以决定是否执行某些部分。
- ifeq / ifneq:条件判断,判断两个值是否相等或不相等。
- ifdef / ifndef:判断某个变量是否已定义。
```makefile
ifeq ($(CC),gcc)
CFLAGS += -O2
else
CFLAGS += -O0
endif
#1. ifeq ($(CC),gcc)
#这个条件判断检查变量 CC 是否等于 gcc,也就是判断使用的编译器是否是 gcc(GNU 编译器)。#如果 CC 的值是 gcc,则执行 ifeq 语句中的代码块,否则执行 else 语句中的代码。
#2. CFLAGS += -O2
#如果编译器是 gcc,就给 CFLAGS 变量添加 -O2 编译选项。-O2 是 GCC 的优化选项,它启用中
#等级别的优化,在编译过程中提高程序的执行效率,但不会过度增加编译时间。
#3. CFLAGS += -O0
#如果编译器不是 gcc,则给 CFLAGS 变量添加 -O0 编译选项。-O0 表示禁用所有优化,通常在调#试阶段使用,这样可以避免优化对调试过程的干扰
objects = main.o utils.o
all: $(objects)
gcc -o myprogram $(objects)
你可以在 Makefile 中使用 include 来导入外部的 Makefile 或配置文件。
include config.mk
make 根据以下步骤处理 Makefile:
.PHONY: clean
#.PHONY 是一个特殊的目标,表示 clean 不是一个文件名,而是一个伪目标。
#即使有一个名为 clean 的文件,Make 也会执行这个规则而不是认为它是一个过期的目标。
CC = gcc
RM = rm
EXE = simple
# 上述都是定义变量
#SRCS = main.c foo.c
SRCS = $(wildcard *.c)
#使用 wildcard 函数来列出当前目录下所有的 .c 文件。这个变量 SRCS 包含所有源文件名(例如:main.c, foo.c)。
# 把.c换成对应的.o
#OBJS = foo.o foo2.o main.o
OBJS = $(patsubst %.c,%.o,$(SRCS))
$(EXE): $(OBJS)
$(CC) -o $@ $^
#这是链接命令,使用 gcc 编译器将目标文件 $(OBJS) 链接成最终的可执行文件。
#$@ 代表规则的目标,这里是 $(EXE)(即 simple)。
#$^ 代表规则的所有依赖文件,这里是 $(OBJS)(即所有的 .o 文件)
%.o: %.c
$(CC) -o $@ -c $^
clean:
$(RM) $(EXE) $(OBJS)
src: # 测试make src显示相应的xx.c
@echo $(SRCS)
objs:# 测试make objs显示相应的xx.o
@echo $(OBJS)
CROSS =
#这里定义了一个名为 CROSS 的变量,但它为空。
#通常,这个变量用于交叉编译(cross-compilation)。例如,如果你需要为不同的架构(比如 ARM、x86、MIPS 等)编译程序,你可以设置 CROSS 为相应的编译前缀,如 arm-linux- 或 x86_64-。
#CROSS 为空,意味着默认使用系统的 gcc 和 g++ 编译器。
# 定义CC为gcc编译
CC = $(CROSS)gcc
# 定义CXX为g++编译
CXX = $(CROSS)g++
# 定义DEBUG 方式为 -g -O2
DEBUG = -g -O2
CFLAGS = $(DEBUG) -Wall -c
RM = rm -rf
# /定义SRC为当前工程目录下所有的.cpp文件
SRCS = $(wildcard ./*.c)
# 定义OBJS为SRCS对应的.o文件
OBJS = $(patsubst %.c, %.o, $(SRCS))
# 定义HEADER_PATH为当前工程中的头文件路径
#-I 是 gcc 和 g++ 编译器的一个选项,用来指定 头文件的搜索路径。
HEADER_PATH = -I ./include/
# 定义LIB_PATH为当前工程中的头文件路径
#-L 是 gcc 和 g++ 编译器的一个选项,用来指定 库文件的搜索路径。
LIB_PATH = -L ./lib/
# 输出当前LIB_PATH中的内容
#$(warning ...) 是 make 的一个内置函数,用来在执行 make 时输出警告信息。
$(warning LIB_PATH)
# 制定LIBS链接库的名称
#用于指定 链接的库。
#-lpthread 是 gcc 和 g++ 的一个选项,用于链接 pthread 库,它提供了多线程相关的功能。
#-l 是一个链接选项,用来指定一个库,pthread 是要链接的库的名称(通常在 Linux 上会是 libpthread.so 或 libpthread.a)。
#作用:在链接阶段,gcc 会链接 pthread 库,允许程序使用多线程功能。
LIBS=-lpthread
# lib中的库文件名称为libpthread.so
# 定义当前生成的版本
VERSION = 1.0.0
# 定义生成可执行文件的名称
TARGET = simple.$(VERSION)
$(TARGET) : $(OBJS)
# 告诉编译器生成可执行文件时库存放的目录,以及库的名字
$(CXX) $^ -o $@ $(LIB_PATH) $(LIBS)
#表示如何从源文件 .c 生成目标文件 .o。它意味着:对于每个 .c 文件,都会生成一个对应的 .o 文件。
$(OBJS):%.o : %.c
#告诉编译器生成中间文件时头文件的所在目录
$(CXX) $(CFLAGS) $< -o $@ $(HEADER_PATH)
clean:
$(RM) $(TARGET) *.o
在 Linux 系统中,Makefile 是用于自动化构建过程的脚本。它并不需要单独的“安装”,因为 Makefile 本身是一个文本文件,而 make 是一个工具,通常已经预安装在大多数 Linux 发行版中。
sudo apt update
sudo apt install make
cmake基于makefile二次开发,难度较小
注释:使用 # 表示注释。
# 这是一个注释
命令调用:CMake 命令不区分大小写,但通常使用小写。
command(arg1 arg2 ...)
变量:使用 ${} 来引用变量。
set(MY_VAR "Hello")
message(${MY_VAR}) # 输出 Hello
变量使用和取值
定义变量:使用 set() 命令定义变量。
set(MY_VAR "Hello World")
set( [CACHE [FORCE]])
:变量名。
:变量的值。
CACHE:将变量存储在 CMake 缓存中,使其在多次运行 CMake 时保持不变。
:变量的类型,常见类型包括 STRING、BOOL、PATH 等。
:变量的描述信息。
FORCE:强制覆盖缓存中的变量值(如果变量已存在)。
引用变量:使用 ${} 引用变量。
message(${MY_VAR}) # 输出 Hello World
环境变量:使用 $ENV{} 引用环境变量。
message($ENV{HOME}) # 输出当前用户的主目录
project():定义项目名称和版本。
project(MyProject VERSION 1.0)
message():打印消息。
message("This is a message")
MESSAGE([] "message text")
:指定消息的类型,常见的模式有:
STATUS:输出状态信息(通常以 -- 开头)。
WARNING:输出警告信息。
FATAL_ERROR:输出错误信息并停止 CMake 的执行。
DEBUG:输出调试信息(仅在调试模式下可见)。
"message text":要输出的消息内容。
add_executable():定义可执行文件。
add_executable(my_app main.cpp)
add_library():定义库(静态库或动态库)。
add_library(my_lib STATIC lib.cpp) # 静态库
add_library(my_lib SHARED lib.cpp) # 动态库
target_link_libraries():链接库到目标。
target_link_libraries(my_app my_lib)
清理工具
cmake .
make
mkdir build
cd build
cmake ..
make
# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)
# 工程,他不是执行文件名
PROJECT(HIT)
# 手动加入文件 ${变量名}} ,比如${SRC_LIST}
SET(SRC_LIST main.c)
set(SRC_LIST2 main2.c)# 关键字无所谓大小写
# MESSAGE和echo类似
MESSAGE(STATUS "PROJECT_BINARY_DIR DIR " ${PROJECT_BINARY_DIR})
# 输出类似:-- PROJECT_BINARY_DIR DIR /path/to/build/directory
MESSAGE(STATUS "PROJECT_SOURCE_DIR DIR " ${PROJECT_SOURCE_DIR})
# 生产执行文件名0voice 0voice2
ADD_EXECUTABLE(0voice ${SRC_LIST})
ADD_EXECUTABLE(0voice2 ${SRC_LIST2})
#ADD_SUBDIRECTORY 命令用于将一个子目录(如 src)添加到当前项目中,并使该子目录中的 CMakeLists.txt 文件被处理。这意味着,CMake 会递归地进入 src 子目录,执行该子目录中的 CMake 配置文件,并生成相关的构建文件(如 Makefile 或 Visual Studio 项目文件)。
# 添加子目录
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)
#这条命令会将 doc/ 目录及其内容安装到 share/doc/cmake/0voice 目录下。
#DIRECTORY:用于指定要安装的目录(doc/)。
#DESTINATION:指定安装的目标目录(share/doc/cmake/0voice)
#这种方式非常适合需要跨平台和模块化管理的项目,尤其是当你需要在多个目录下组织代码或将项目安装到指定位置时
将子目录编译成库文件,然后再给到上一级进行链接
HIT/ # 项目的根目录
├── CMakeLists.txt # 项目的 CMake 配置文件
├── src/ # 存放源代码的子目录
│ ├── CMakeLists.txt # src 子目录的 CMake 配置文件
│ ├── dir1/ # 子目录 1
│ │ ├── CMakeLists.txt # dir1 的 CMake 配置文件
│ │ └── dir1.c # dir1 子目录中的源文件
│ └── dir2/ # 子目录 2
│ ├── CMakeLists.txt # dir2 的 CMake 配置文件
│ └── dir2.c # dir2 子目录中的源文件
└── doc/ # 文档文件夹
├── README.md # 项目的文档
└── COPYRIGHT # 项目的版权信息
ADD_LIBRARY (dir1 SHARED ${DIR_SRCS})
2. dir2中CMakeLists.txt
```makefile
AUX_SOURCE_DIRECTORY(. DIR_SRCS)
ADD_LIBRARY(dir2 ${DIR_SRCS})
#同上
#CMAKE_CURRENT_SOURCE_DIR 是 CMake 内置的变量,表示当前 CMake 配置文件所在的目录。
INCLUDE_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}/dir1")
INCLUDE_DIRECTORIES(dir1)
MESSAGE(STATUS "CMAKE_CURRENT_SOURCE_DIR -> " ${CMAKE_CURRENT_SOURCE_DIR})
ADD_SUBDIRECTORY("${CMAKE_CURRENT_SOURCE_DIR}/dir1")
INCLUDE_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}/dir2")
ADD_SUBDIRECTORY("${CMAKE_CURRENT_SOURCE_DIR}/dir2")
ADD_EXECUTABLE(li ${SRC_LIST} )# 创建一个可执行文件li
TARGET_LINK_LIBRARIES(li dir1 dir2)
#将库文件链接到可执行文件。此处将 dir1 和 dir2 库链接到 li可执行文件。这样,lili就可以使用 dir1 和 dir2 中定义的函数和符号。
INSTALL(TARGETS li RUNTIME DESTINATION bin)
4. 整体CMakeLists.txt
```makefile
# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)
PROJECT(0VOICE)
ADD_SUBDIRECTORY(src)
# FILES代表文件
# DIRECTORY 代表目录
# INSTALL(FILES README.md COPYRIGHT DESTINATION share/doc/cmake/0voice)
INSTALL(DIRECTORY doc/ DESTINATION share/doc/cmake/0voice)
MyProject/
├── CMakeLists.txt # 根目录 CMake 配置文件
├── lib1/ # 第一个子目录,生成静态库
│ ├── CMakeLists.txt # lib1 子目录的 CMake 配置文件
│ └── lib1.cpp # lib1 的源文件
├── lib2/ # 第二个子目录,生成动态库
│ ├── CMakeLists.txt # lib2 子目录的 CMake 配置文件
│ └── lib2.cpp # lib2 的源文件
└── main.cpp # 主源文件,链接到 lib1 和 lib2
project(MyProject)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
add_subdirectory(lib1)
add_subdirectory(lib2)
add_executable(my_app main.cpp)
target_link_libraries(my_app PRIVATE lib1 lib2)
3. lib1/CMakeLists.txt (生成静态库)
在 lib1 目录中,创建一个静态库(lib1.a)。
```makefile
# lib1 的 CMake 配置文件
# 查找 lib1 目录下的所有源文件
set(LIB1_SRC lib1.cpp)
# 创建静态库 lib1
add_library(lib1 STATIC ${LIB1_SRC})
# 如果有头文件目录需要添加,可以在这里使用 INCLUDE_DIRECTORIES
# 例如:INCLUDE_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}/include")
# lib2 的 CMake 配置文件
# 查找 lib2 目录下的所有源文件
set(LIB2_SRC lib2.cpp)
# 创建动态库 lib2
add_library(lib2 SHARED ${LIB2_SRC})
# 如果有头文件目录需要添加,可以在这里使用 INCLUDE_DIRECTORIES
# 例如:INCLUDE_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}/include")
#include <iostream>
#include "lib1.h" // 假设 lib1 提供了一个 lib1.h 头文件
#include "lib2.h" // 假设 lib2 提供了一个 lib2.h 头文件
int main() {
std::cout << "Hello from main!" << std::endl;
// 使用 lib1 和 lib2 中的函数
lib1_function();
lib2_function();
return 0;
}
ZLToolKit/
├── CMakeLists.txt # 根目录 CMake 配置文件
├── src/ # 源代码目录
│ ├── CMakeLists.txt # src 子目录的 CMake 配置文件
│ ├── lib1.cpp # lib1 的源文件
│ ├── lib2.cpp # lib2 的源文件
│ └── main.cpp # 主程序
└── build/ # 用于构建的目录(运行 CMake 和 make 时生成)
cmake_minimum_required(VERSION 3.10)
# 设置项目名称
project(ZLToolKit)
# 设置 C++ 标准
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
# 设置调试debug和发布release版本的输出目录
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/debug_bin)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/release_bin)
# 添加 src 子目录
add_subdirectory(src)
# 设置构建类型:如果没有指定,默认是 "Release"
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE)
endif()
# 打印出当前的构建类型
message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
# src 子目录的 CMake 配置文件
# 设置源文件
set(SRC_LIST main.cpp lib1.cpp lib2.cpp)
# 创建可执行文件
add_executable(ZLToolKitApp ${SRC_LIST})
# 设置调试和发布版本的编译选项
target_compile_options(ZLToolKitApp PRIVATE
"$<$<CONFIG:Debug>:-g>"
"$<$<CONFIG:Release>:-O3>"
)
#target_compile_options 用于指定目标(ZLToolKitApp)的编译选项。PRIVATE 表示这些编译选项只对目标 ZLToolKitApp 有效。
#$<$<CONFIG:Debug>:-g>:这表示如果构建类型是 Debug,则编译时添加 -g 选项,通常用于生成调试符号。
#$<$<CONFIG:Release>:-O3>:这表示如果构建类型是 Release,则编译时添加 -O3 选项,通常用于优化代码(更高的优化级别)。
#target_compile_options 是通过 generator expressions 来动态选择不同的编译选项。$<$<CONFIG:Debug>:-g> 是 CMake 的条件语法,意思是,如果当前构建类型是 Debug,则使用 -g 选项。
# 设置 CMake 自动选择链接库和头文件
target_include_directories(ZLToolKitApp PRIVATE ${CMAKE_SOURCE_DIR}/include)
# 根据构建类型设置不同的链接库
target_link_libraries(ZLToolKitApp PRIVATE
"$<$<CONFIG:Debug>:/path/to/debug/lib>"
"$<$<CONFIG:Release>:/path/to/release/lib>"
)
#include <iostream>
#include "lib1.h" // 假设 lib1 提供了一个 lib1.h 头文件
#include "lib2.h" // 假设 lib2 提供了一个 lib2.h 头文件
int main() {
std::cout << "Hello from ZLToolKit!" << std::endl;
// 使用 lib1 和 lib2 中的函数
lib1_function();
lib2_function();
return 0;
}
首先在根目录创建一个构建目录,并切换到该目录:
mkdir build
cd build
生成构建文件
运行 CMake 配置命令:
cmake ..
这时 CMake 会根据 CMakeLists.txt 配置文件生成 Makefile(或适用于系统的构建文件)。
构建项目
然后运行 make 来编译项目:
make
CMake 会根据所选择的构建类型(Debug 或 Release)进行编译。如果没有手动指定构建类型,默认是 Release。
调试版本 vs 发布版本
CMake 会自动根据 CMAKE_BUILD_TYPE 变量切换到 Debug 或 Release 配置:
Debug 版本:会添加 -g 编译选项生成调试符号,并将输出文件放在 ${CMAKE_BINARY_DIR}/debug_bin 目录中。
Release 版本:会添加 -O3 编译选项进行优化,并将输出文件放在 ${CMAKE_BINARY_DIR}/release_bin 目录中。
例如,要生成 Debug 版本,执行:
cmake -DCMAKE_BUILD_TYPE=Debug ..
make
或者,要生成 Release 版本:
cmake -DCMAKE_BUILD_TYPE=Release ..
make