0%

makefile文件命令

一、makefile

  • 先给个编译二进制的示例
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
CC = gcc
CXX = g++
CFLAGS = -Wall -Werror -Wshadow -std=c11
CXXFLAGS += -DLINUX -DUSE_SPDLOG -Wall -Wshadow -std=c++14
LFLAGS =

#要编译的目标
TARGET = run
#源文件路径,跟makefile文件同目录不用填,目录使用/结束
SRCDIR = ./app/ ./utils/
#这些HEADERS将被安装到 $(PREFIX_INC) 目录下面,供其他模块使用,多个头文件空格分开
HEADERS = -I$(PREFIX_INC)
#编译中间文件路径,跟SRCDIR目录不用填,目录使用/结束
OBJDIR =
#链接库名,带上-l,-lsvpn 静态库填写绝对路径
LIB = -pthread -ldl ../build/output/libspdlogd.a #-lmylib -lcurl ../threepart/libhv/lib/libhv.a
#引用头文件路径,带上-I, -I$(PREFIX_INC)
INCLUDE =

# gcov
ifeq ($(debug), 1)
CFLAGS := $(filter-out -O2, $(CFLAGS))
CXXFLAGS := $(filter-out -O2, $(CXXFLAGS))
CFLAGS += -g -DDEBUG_GLOBAL --coverage
CXXFLAGS += -g -DDEBUG_GLOBAL --coverage
LFLAGS += --coverage
endif

#如果为空,就是当前目录
ifeq ($(SRCDIR),)
SRCDIR += ./
endif

#下面四个命令通过模式匹配获取当前目录下的所有C,CPP,O文件
CPP_SOURCES = $(foreach d,$(SRCDIR),$(wildcard $(d)*.cpp))
C_SOURCES = $(foreach d,$(SRCDIR),$(wildcard $(d)*.c))
CPP_OBJS = $(patsubst %.cpp, $(OBJDIR)%.o, $(CPP_SOURCES))
C_OBJS = $(patsubst %.c, $(OBJDIR)%.o, $(C_SOURCES))

#下面把一些其他目录下的源文件也放到里面
SOURCES += $(CPP_SOURCES) $(C_SOURCES) $(OBJS:%.o=%.c*)
OBJS += $(CPP_OBJS) $(C_OBJS)
ALL_GCNO = $(OBJS:%.o=%.gcno)
ALL_GCDA = $(OBJS:%.o=%.gcda)

.PHONY: clean all cp cpp cpso _clean _all _install test

.SUFFIXES: .c .cpp .o

.c.o:
$(CC) -c $*.c -o $*.o $(INCLUDE) $(CFLAGS)
.cpp.o:
$(CXX) -c $*.cpp -o $*.o $(INCLUDE) $(CXXFLAGS)


all: $(OUTDIR)$(TARGET) $(_all)

header:
ifneq ($(HEADERS),)
install -d $(PREFIX_INC)
install --verbose --mode=0644 $(HEADERS) $(PREFIX_INC)
endif

$(OUTDIR)$(TARGET): $(OBJS)
ifeq ($(CPP_OBJS),)
$(CC) $(LFLAGS) -o $(OUTDIR)$(TARGET) $(OBJS) -L$(PREFIX_LIB) $(LIB)
else
$(CXX) $(LFLAGS) -o $(OUTDIR)$(TARGET) $(OBJS) -L$(PREFIX_LIB) $(LIB)
endif
make header

install: all $(_install) $(HEADERS)
ifneq ($(debug), 1)
strip $(OUTDIR)$(TARGET)
endif
install -d $(INSTALL_TARGETDIR)
install --verbose --mode=0755 $(OUTDIR)$(TARGET) $(INSTALL_TARGETDIR)

cp: $(_cp)

cppscan:clean
install -d $(CPPTEST_WORKSPACE)/$(TARGET)
cpptesttrace --cpptesttraceOutputFile=$(TARGET).bdf --cpptesttraceProjectName=$(TARGET) make || echo
cpptestcli -data $(CPPTEST_WORKSPACE) -resource $(TARGET) -bdf $(TARGET).bdf -config user://mustfix -report $(CPPTEST_REPORT)/$(REPORTDIR) || echo

clean: $(_clean)
rm -f $(OUTDIR)$(TARGET) $(OBJS) $(TARGET).bdf $(ALL_GCNO) $(ALL_GCDA)
rm -f gcov.zip *.gcda *.gcno
rm -f $(OUTDIR)$(TARGET)_test ./test/*.o

gcov:
$(LCOV_GCOV_TOOL) `pwd`
  • 编译so库的示例
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
CC = gcc
CXX = g++
CFLAGS = -fPIC -Wall -Werror -Wshadow -std=c11
CXXFLAGS = -fPIC -Wall -Wshadow -std=c++14
LFLAGS = -shared -fPIC

#要编译的目标
TARGET = skf.so
#源文件路径,跟makefile文件同目录不用填,目录使用/结束
SRCDIR = ./
#这些HEADERS将被安装到 $(PREFIX_INC) 目录下面,供其他模块使用,多个头文件空格分开
HEADERS = -I$(PREFIX_INC)
#编译中间文件路径,跟SRCDIR目录不用填,目录使用/结束
OBJDIR =
#链接库名,带上-l,-lsvpn 静态库填写绝对路径
LIB = -pthread -ldl ../build/output/libspdlogd.a #-lmylib -lcurl ../threepart/libhv/lib/libhv.a
#引用头文件路径,带上-I, -I$(PREFIX_INC)
INCLUDE =

# gcov
ifeq ($(debug), 1)
CFLAGS := $(filter-out -O2, $(CFLAGS))
CXXFLAGS := $(filter-out -O2, $(CXXFLAGS))
CFLAGS += -g -DDEBUG_GLOBAL --coverage
CXXFLAGS += -g -DDEBUG_GLOBAL --coverage
LFLAGS += --coverage
endif

#如果为空,就是当前目录
ifeq ($(SRCDIR),)
SRCDIR += ./
endif

#下面四个命令通过模式匹配获取当前目录下的所有C,CPP,O文件
CPP_SOURCES = $(foreach d,$(SRCDIR),$(wildcard $(d)*.cpp))
C_SOURCES = $(foreach d,$(SRCDIR),$(wildcard $(d)*.c))
CPP_OBJS = $(patsubst %.cpp, $(OBJDIR)%.o, $(CPP_SOURCES))
C_OBJS = $(patsubst %.c, $(OBJDIR)%.o, $(C_SOURCES))

#下面把一些其他目录下的源文件也放到里面
SOURCES += $(CPP_SOURCES) $(C_SOURCES) $(OBJS:%.o=%.c*)
OBJS += $(CPP_OBJS) $(C_OBJS)
ALL_GCNO = $(OBJS:%.o=%.gcno)
ALL_GCDA = $(OBJS:%.o=%.gcda)

.PHONY: clean all cp cpp cpso _clean _all _install test

.SUFFIXES: .c .cpp .o

.c.o:
$(CC) -c $*.c -o $*.o $(INCLUDE) $(CFLAGS)
.cpp.o:
$(CXX) -c $*.cpp -o $*.o $(INCLUDE) $(CXXFLAGS)


all: $(OUTDIR)$(TARGET) $(_all)

header:
ifneq ($(HEADERS),)
install -d $(PREFIX_INC)
install --verbose --mode=0644 $(HEADERS) $(PREFIX_INC)
endif

$(OUTDIR)$(TARGET): $(OBJS)
ifeq ($(CPP_OBJS),)
$(CC) $(LFLAGS) -o $(OUTDIR)$(TARGET) $(OBJS) -L$(PREFIX_LIB) $(LIB)
else
$(CXX) $(LFLAGS) -o $(OUTDIR)$(TARGET) $(OBJS) -L$(PREFIX_LIB) $(LIB)
endif
make header

install: all $(_install) $(HEADERS)
ifneq ($(debug), 1)
strip $(OUTDIR)$(TARGET)
endif
install -d $(INSTALL_TARGETDIR)
install --verbose --mode=0755 $(OUTDIR)$(TARGET) $(INSTALL_TARGETDIR)

cp: $(_cp)

cppscan:clean
install -d $(CPPTEST_WORKSPACE)/$(TARGET)
cpptesttrace --cpptesttraceOutputFile=$(TARGET).bdf --cpptesttraceProjectName=$(TARGET) make || echo
cpptestcli -data $(CPPTEST_WORKSPACE) -resource $(TARGET) -bdf $(TARGET).bdf -config user://mustfix -report $(CPPTEST_REPORT)/$(REPORTDIR) || echo

clean: $(_clean)
rm -f $(OUTDIR)$(TARGET) $(OBJS) $(TARGET).bdf $(ALL_GCNO) $(ALL_GCDA)
rm -f gcov.zip *.gcda *.gcno
rm -f $(OUTDIR)$(TARGET)_test ./test/*.o

gcov:
$(LCOV_GCOV_TOOL) `pwd`

1. 默认规则

1
2
default :
make run
  • 添加默认规则,直接使用make将执行default下的命令

2. 文件生成

1
2
3
4
5
6
# 文件生成规则
ipl.bin : ipl.nas Makefile
../z_tools/nask.exe ipl.bin ipl.lst

helloOS.img : ipl.bin Makefile
../z_tools/edimg.exe imgin:../z_tools/fdimg0at.tek
  • #代表注释
  • ipl.bin : ipl.nas Makefile代表需要制作ipl.bin文件需要ipl.nasMakefile,如果都有,执行下面的语句
  • \代表续行符号

3. 变量使用

1
2
3
4
5
6
7
TOOLPATH    = ../z_tools
MAKE = $(TOOLPATH)/make.exe -r
NASK = $(TOOLPATH)/nask.exe
EDIMG = $(TOOLPATH)/edimg.exe
IMGTOL = $(TOOLPATH)/imgtol.com
COPY = copy
DEL = del

4. 运行规则

  1. 先根据编译命令寻找规则
  2. 找到规则后,匹配后面的依赖
  3. 依赖匹配按照从上向下匹配,两个规则均满足,使用第一个
  4. 如果依赖不满足,根据依赖继续找依赖的规则
  5. 依赖满足,根据后面的命令进行编译

5. 内置变量符号含义

5.1. 匹配符%和通配符*的区别

所以虽然两个符号的意思有点沾边,但是他们的工作方式时完全不一样。

匹配符%的意思

  1. 我要找test1.o的构造规则,看看Makefile中那个规则符合。
  2. 然后找到了 %.o : %.c
  3. 来套一下来套一下:
  4. %.o 和我要找的 test1.o 匹配
  5. 套上了,得到%=test1。
  6. 所以在后面的 %.c 就表示 test1.c 了。
  7. OK进行构造

通配符*的意思

  1. 我不知道目标的名字,系统该目录下中所有后缀为.c的文件都是我要找的。
  2. 然后遍历目录的文件,看是否匹配。找出所有匹配的项目。

5.2. 特殊符号

  • $@: 目标的名字
  • $^: 构造所需文件列表所有所有文件的名字
  • $<: 构造所需文件列表的第一个文件的名字
  • $?: 构造所需文件列表中更新过的文件
  • $*: 匹配的目标模式中%及前面的部分,如: dir/a.foo.ba.%.b匹配,$*代表dir/a.foo

5.3. =:=?=+=

1) = 整体拉通赋值

1
2
3
VAR_A = A
VAR_B = ${VAR_A} B
VAR_A = $(VAR_B) A
  • 这样写makefile就会报错,因为循环赋值,最终makefile不知道怎么拉通了
1
2
3
VAR_A = A
VAR_B = ${VAR_A} B
VAR_A = AA

2) := 当前此位置的值赋值

1
2
3
VAR_A = A
VAR_B := ${VAR_A} B
VAR_A = AA
  • 当前位置的值直接赋值,这里的VAR_B会等于当前到这个位置时的值A B
1
2
3
4
5
VAR_C = C
VAR_A = $(VAR_C) A
VAR_B := $(VAR_A) B
VAR_A = $(VAR_C) AA
VAR_C = CC
  • 这里的VAR_B会等于当前到这个位置时的值C A B,但是VAR_ACC AA

3) ?= 没赋值就赋值

1
2
VAR_C = C
VAR_C ?= CC
  • 最终VAR_CC

4) += 新增

  • 就是新增,没什么多余东西

6. 内置函数

6.1. foreach

  • 用var遍历list,在text中对var进行操作
1
$(foreach <var>,<list>,<text>)

示例

1
C_SOURCES = $(foreach d,$(SRCDIR),$(wildcard $(d)*.c))
  • 遍历SRCDIR,每个元素给到d中,然后执行$(wildcard $(d)*.c)输出
  • 因为是$(d)*c,所以$(SRCDIR)每个目录要以/结尾

6.2. wildcard

  • 在Makefile规则中,通配符会被自动展开。但在变量的定义和函数引用时,通配符将失效。这种情况下如果需要通配符有效,就需要使用函数“wildcard”
  • 它的用法是:$(wildcard PATTERN...)
  • Makefile中,它被展开为已经存在的、使用空格分开的、匹配此模式的所有文件列表。

6.3. patsubst

  • 替换列表中的字符串
1
$(patsubst 原模式, 目标模式, 文件列表)

示例

1
C_OBJS = $(patsubst %.c, $(OBJDIR)%.o, $(C_SOURCES))
  • C_SOURCES列表文件,每一个执行%.c$(OBJDIR)%.o的替换

6.4. echo

1
2
3
4
img :
@echo $(SRCDIR)
@echo $(C_SOURCES)
$(MAKE) haribote.img

6.5. filter-out 从空格分开的数组中过滤元素

  • 经过尝试只能字符串匹配,不能正则
1
2
3
4
5
6
7
8
SRCDIR = ./ ./lib/ ./init/

C_SOURCES = $(foreach d,$(SRCDIR),$(wildcard $(d)*.c))
C_SOURCES_1 = $(filter-out ./init/main.c, $(C_SOURCES))

all:
@echo "c_source $(C_SOURCES)"
@echo "filtered c_source $(C_SOURCES_1)"

输出

1
2
c_source ./bootpack.c  ./init/main.c
filtered c_source ./bootpack.c

踩坑记

  • Makefile需要用tab而非空格进行缩进,否则会报*** multiple target patterns. Stop.

二、CMakeLists.txt

1. 一些基本命令

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
########## 工程定义 ##########
# cmake最小支持版本3.8
CMAKE_MINIMUM_REQUIRED(VERSION 3.8)

# 项目工程名字
PROJECT(Clion_cppStudy
LANGUAGES C CXX
VERSION 1.0.0.0
DESCRIPTION "Diy c++ project"
)

########## 变量 ##########
# 设置lib库生成路径
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/build/lib)
# 设置bin文件生成路径
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/build/bin
# 编译命令导出json给其他地方使用,vim的ycm可以使用作为代码提示
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# 设置C++标准
set(CMAKE_CXX_STANDARD 11)
# 设置符号隐藏
set(CMAKE_C_VISIBILITY_PRESET hidden)
set(CMAKE_CXX_VISIBILITY_PRESET hidden)

########## 选项 ##########
# 设置选项
option(RELEASE "Build release, default is OFF" OFF)
# 根据选项指定编译类型
# 也可以使用 -DCMAKE_BUILD_TYPE:STRING=RELEASE 来指定,默认为RELEASE
# 如果文件中有SET,使用选项将无效
if(RELEASE)
SET(CMAKE_BUILD_TYPE "RELEASE")
else(RELEASE)
SET(CMAKE_BUILD_TYPE "DEBUG")
endif(RELEASE)

# 添加子目录
add_subdirectory(src)

# FILE生成的是绝对路径文件
# GLOB和GLOB_RECUSE语法一样,但是一个会对子目录匹配,一个不会匹配子目录
# 两种方式都无法检测文件变化,如果文件变化需要重新config工程
# 匹配app和utils一级目录的c开头后缀的文件
FILE(GLOB SRC_FILES app/*.c* utils/*.c*)
# 匹配app和utils所有子目录的c开头后缀的文件
FILE(GLOB_RECURSE SRC_FILES app/*.c* utils/*.c*)

# aux_source_directory生成的是相对路径
# aux_source_directory(<dir> <variable>) 将dir下面的所有源文件储存到变量(追加)
AUX_SOURCE_DIRECTORY(./app SRC_FILES)
AUX_SOURCE_DIRECTORY(./utils SRC_FILES)
AUX_SOURCE_DIRECTORY(./common COMMON_SRC_FILES)
# 合并两个列表的数据
list(APPEND SRC_FILES ${COMMON_SRC_FILES})
# 删除列表中几个数据
list(REMOVE_ITEM SRC_FILES ${COMMON_SRC_FILES})

# 将变量指定的源文件编译成可执行文件main
add_executable(${BIN_NAME} ${SRC_FILES})
# SHARED生成动态库,STATIC生成静态库
add_library(${LIB_NAME} SHARED ${SRC_FILES})

# 给可执行文件编译设置头文件目录
target_include_directories(${BIN_NAME} PRIVATE ./includes ../includes)

# cmake的选项,命令行使用 -DBUILD_TESTING=YES
option(BUILD_TESTING "Generate test project, default is YES" YES)
# 判断是否为win32并且使用msvc,可以直接使用WIN32和MSVC判断,不用定义
if(WIN32 AND MSVC)
# 添加编译选项
add_compile_options(
/wd4005 # thrift warning: WIN32_LEAN_AND_MEAN redefined
/wd4018 # thrift warning: signed and unsigned not match
/wd4244 # thrift warning: translate uint64_t to std::size_t
/wd4267 # thrift warning: 'argument' : conversion from 'size_t' to 'type', possible loss of data
/wd4275 # thrift warning: 'argument' : no matching function for call
/wd4284 # thrift warning: 'argument' : conversion from 'type1' to 'type2', possible loss of data
/wd4305 # thrift warning: 'argument' : truncation from 'type1' to 'type2'
/wd4819 # thrift warning: 'argument' : The file contains a character that cannot be represented in the current code page.
/wd4996 # thrift warning: 'argument' : This function or variable may be unsafe
)
# 添加链接选项
add_link_options(
/MAP
)
endif()

########## 逻辑相关 ##########
if(WIN32)
# 提前退出此cmake文件,不编译后续代码
return()
endif()

1.1. if 条件判断

1
2
3
4
5
6
7
8
9
10
11
# 判断是否为win32环境
if(WIN32)
return()
elseif(UNIX)
return()
endif()

# 判断非win32环境,NOT必须大写
if(NOT WIN32)
return()
endif()

2. 内置变量汇总

  • PROJECT_SOURCE_DIR: 项目工程目录
  • EXCUTABLE_OUTPUT_PATH: 可执行文件输出目录
  • LIBRARY_OUTPUT_PATH: 动态库输出目录
  • CMAKE_CXX_STANDARD: C++标准
  • CMAKE_HOST_SYSTEM_NAME: 调用cmake命令的系统名称
    • Linux
    • Windows
    • Darwin
  • CMAKE_SYSTEM_NAME: 非跨平台就是CMAKE_HOST_SYSTEM_NAME,跨平台开启时,参考 开启cross compiling

3. 修改CMakeCache.list的变量

三种方式修改变量

参数的方式修改

详情看cmake --help

修改CMakeCache.txt

有gui有文本直接修改

添加cmake配置修改

  • 修改cmakecache的方式不好做到继承,项目内置cmake配置的方式最方便
  • 如果变量为INTERNAL类型,无法通过CMakeLists.txt来修改,需要像下面这样

添加一个文件Preload.cmake,此文件会被cmake执行前先加载

1
2
3
4
5
6
7
# SET加参数CACHE说明是修改cmakecache的变量
# SET(CMAKE_GENERATOR "MinGW Makefiles" CACHE INTERNAL "使用mingw作为默认编译" FORCE)
if(WIN32)
SET(CMAKE_GENERATOR "MinGW Makefiles" CACHE INTERNAL "使用mingw作为默认编译" FORCE)
# SET(CMAKE_GENERATOR "Visual Studio 14 2015" CACHE INTERNAL "使用vs 2015作为默认编译" FORCE)
# SET(CMAKE_SYSTEM_VERSION "10.0.17763" CACHE INTERNAL "定义windows sdk为10.0" FORCE)
endif()

4. 重点方法详解

4.1. target_include_directories 添加头文件

1
2
3
target_include_directories(<target> [SYSTEM] [AFTER|BEFORE]
<INTERFACE|PUBLIC|PRIVATE> [items1...]
[<INTERFACE|PUBLIC|PRIVATE> [items2...] ...])
  • 真实作用就是给编译选项加-I
  • items可以使用$<...>的方式添加,比如使用$<TARGET_PROPERTY:run,SOURCE_DIR>获取run的源码路径

有关INTERFACE|PUBLIC|PRIVATE的解释

  • 如果源文件(例如CPP)中包含第三方头文件,但是头文件(例如hpp)中不包含该第三方文件头,采用PRIVATE。
  • 如果源文件和头文件中都包含该第三方文件头,采用PUBLIC。
    • 对于动态库来说,如果设置头文件为PUBLIC,外部在link此动态库时,会自动添加头文件目录到自己的include中
  • 如果头文件中包含该第三方文件头,但是源文件(例如CPP)中不包含,采用 INTERFACE。
1
2
3
target_link_libraries(<target>
<PRIVATE|PUBLIC|INTERFACE> <item>...
[<PRIVATE|PUBLIC|INTERFACE> <item>...]...)

item取值

  1. target名字,为cmake工程中有add_library()定义的
  2. 原始名字,cmake会查找链接库路径中是否有相应的库,比如设定dl会添加-ldl
  3. 全路径,库的全路径
  • 真实作用就是给编译选项加-I
  • items可以使用$<...>的方式添加,比如使用$<TARGET_PROPERTY:run,SOURCE_DIR>获取run的源码路径

有关INTERFACE|PUBLIC|PRIVATE的解释

  • 如果源文件(例如CPP)中包含第三方头文件,但是头文件(例如hpp)中不包含该第三方文件头,采用PRIVATE。
  • 如果源文件和头文件中都包含该第三方文件头,采用PUBLIC。
  • 如果头文件中包含该第三方文件头,但是源文件(例如CPP)中不包含,采用 INTERFACE。

4.3. get_target_property 获取某个target的属性

1
get_target_property(<VAR> target property)

几个常用的property

  • SOURCE_DIR: 源代码根目录
  • SOURCES: 编译此target使用的所有源文件

4.4. add_dependencies 添加依赖

  • 给targetA添加targetB的依赖,targetB先编译完成才能编译targetA
1
add_dependencies(<target> <dependency>...)

4.5. target_compile_definitions 添加编译定义

1
2
3
target_compile_definitions(<target>
<INTERFACE|PUBLIC|PRIVATE> [items1...]
[<INTERFACE|PUBLIC|PRIVATE> [items2...] ...])

示例

1
2
3
4
5
target_compile_definitions(foo PUBLIC FOO)
target_compile_definitions(foo PUBLIC -DFOO) # -D removed
target_compile_definitions(foo PUBLIC "" FOO) # "" ignored
target_compile_definitions(foo PUBLIC -D FOO) # -D becomes "", then ignored
target_compile_definitions(foo PUBLIC FOO=1)

4.6. list 操作数组变量

1) 正则匹配过滤文件

1
list(FILTER <list> <INCLUDE|EXCLUDE> REGEX <regular_expression>)
  • regular_expression是真的正则表达式,和上面的file命令不同

示例

1
2
3
4
# 将test_run所有的源码文件赋值给sources_files
get_target_property(source_files test_run SOURCES)
# 从source_files排除main.cpp这个文件
list(FILTER source_files EXCLUDE REGEX ".*/main.cpp$")

5. 跨平台

  • CMAKE_SYSTEM_NAME主要展示当前执行cmake命令的系统,可以用来做判断系统,但是注意是否开启跨平台
  • WIN32自然是windows的情况
  • ANDROID先定义是由于一般编译安卓的机器可能是linux或windows,所以要在前面判断
1
2
3
4
5
6
7
8
9
10
11
12
13
if (WIN32)
# windows
elseif (APPLE)
if (${DARWIN_TARGET_OS_NAME} MATCHES "ios")
# 苹果手机
elseif (${DARWIN_TARGET_OS_NAME} MATCHES "mac")
# mac电脑
endif ()
elseif (ANDROID)
# android手机
elseif (CMAKE_SYSTEM_NAME MATCHES "Linux")
# linux
endif ()

6. 编译库

6.1. 去除前面的lib前缀

  • 使用下面的命令,gcc编译就会编译出aaa.so而不是libaaa.so
1
2
add_library(aaa SHARED ${SRC_FILES})
set_target_properties(aaa PROPERTIES PREFIX "")

7. 单测

7.1. 配置步骤

  1. 在工程根目录的CMakeLists.txt添加
1
enable_testing()
  1. 在对应的单测可执行文件添加的地方添加单测用例
1
2
3
4
5
6
# 定义添加二进制
add_executable(${BIN_NAME} ${src_files})
# 添加单测用例
add_test(NAME ${BIN_NAME}
COMMAND $<TARGET_FILE:${BIN_NAME}>
)

6.2. 运行步骤

1
2
3
4
# 编译所有二进制
cmake --build build --config Debug -j 4
# 运行单测,使用test可以运行所有的add_test添加的命令
cmake --build build --target test

踩坑记

1. windows下cmake默认指定了msvc作为编译器,想要修改为mingw

参考上面如何修改cmakecache的方法,修改CMAKE_GENERATORMinGW Makefiles