一、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 SRCDIR = ./app/ ./utils/ HEADERS = -I$(PREFIX_INC) OBJDIR = LIB = -pthread -ldl ../build/output/libspdlogd.a INCLUDE = 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 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 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 SRCDIR = ./ HEADERS = -I$(PREFIX_INC) OBJDIR = LIB = -pthread -ldl ../build/output/libspdlogd.a INCLUDE = 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 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. 默认规则 添加默认规则,直接使用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.nas
和Makefile
,如果都有,执行下面的语句\
代表续行符号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. 运行规则 先根据编译命令寻找规则 找到规则后,匹配后面的依赖 依赖匹配按照从上向下匹配,两个规则均满足,使用第一个 如果依赖不满足,根据依赖继续找依赖的规则 依赖满足,根据后面的命令进行编译 5. 内置变量符号含义 5.1. 匹配符%和通配符*的区别 所以虽然两个符号的意思有点沾边,但是他们的工作方式时完全不一样。
匹配符%的意思
我要找test1.o的构造规则,看看Makefile中那个规则符合。 然后找到了 %.o : %.c
来套一下来套一下: %.o
和我要找的 test1.o
匹配套上了,得到%=test1。 所以在后面的 %.c
就表示 test1.c
了。 OK进行构造 通配符*的意思
我不知道目标的名字,系统该目录下中所有后缀为.c的文件都是我要找的。 然后遍历目录的文件,看是否匹配。找出所有匹配的项目。 5.2. 特殊符号 $@
: 目标的名字$^
: 构造所需文件列表所有所有文件的名字$<
: 构造所需文件列表的第一个文件的名字$?
: 构造所需文件列表中更新过的文件$*
: 匹配的目标模式中%及前面的部分,如: dir/a.foo.b
被a.%.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_A
为CC AA
3) ?=
没赋值就赋值 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
实例 1. 根据编译机器的32位和64位的不同处理 1 2 3 4 5 6 7 8 9 ARCH := $(shell uname -m) ifeq ($(ARCH) , i686) @echo "32 bit build" else ifeq ($(ARCH) , x86_64) @echo "64 bit build" endif
踩坑记 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_MINIMUM_REQUIRED (VERSION 3.8 )PROJECT (Clion_cppStudy LANGUAGES C CXX VERSION 1.0 .0.0 DESCRIPTION "Diy c++ project" ) set (LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR} /build/lib)set (EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR} /build/binset (CMAKE_EXPORT_COMPILE_COMMANDS ON )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 )if (RELEASE) SET (CMAKE_BUILD_TYPE "RELEASE" ) else (RELEASE) SET (CMAKE_BUILD_TYPE "DEBUG" ) endif (RELEASE)add_subdirectory (src)FILE (GLOB SRC_FILES app/*.c* utils/*.c*)FILE (GLOB_RECURSE SRC_FILES app/*.c* utils/*.c*)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} )add_executable (${BIN_NAME} ${SRC_FILES} )add_library (${LIB_NAME} SHARED ${SRC_FILES} )target_include_directories (${BIN_NAME} PRIVATE ./includes ../includes)option (BUILD_TESTING "Generate test project, default is YES" YES)if (WIN32 AND MSVC) add_compile_options ( /wd4005 /wd4018 /wd4244 /wd4267 /wd4275 /wd4284 /wd4305 /wd4819 /wd4996 ) add_link_options ( /MAP ) endif ()if (WIN32) return () endif ()
1.1. if 条件判断 1 2 3 4 5 6 7 8 9 10 11 if (WIN32) return () elseif (UNIX) return () endif ()if (NOT WIN32) return () endif ()
2. 内置变量汇总 PROJECT_SOURCE_DIR
: 项目工程目录EXCUTABLE_OUTPUT_PATH
: 可执行文件输出目录LIBRARY_OUTPUT_PATH
: 动态库输出目录CMAKE_CXX_STANDARD
: C++标准CMAKE_HOST_SYSTEM_NAME
: 调用cmake命令的系统名称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 if (WIN32) SET (CMAKE_GENERATOR "MinGW Makefiles" CACHE INTERNAL "使用mingw作为默认编译" 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。 4.2. target_link_libraries 添加动态链接库 1 2 3 target_link_libraries (<target > <PRIVATE|PUBLIC|INTERFACE> <item>... [<PRIVATE|PUBLIC|INTERFACE> <item>...]...)
item取值
target名字,为cmake工程中有add_library()
定义的 原始名字,cmake会查找链接库路径中是否有相应的库,比如设定dl
会添加-ldl
全路径,库的全路径 真实作用就是给编译选项加-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) target_compile_definitions (foo PUBLIC "" FOO) target_compile_definitions (foo PUBLIC -D FOO) 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 get_target_property (source_files test_run SOURCES)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) elseif (APPLE) if (${DARWIN_TARGET_OS_NAME} MATCHES "ios" ) elseif (${DARWIN_TARGET_OS_NAME} MATCHES "mac" ) endif () elseif (ANDROID) elseif (CMAKE_SYSTEM_NAME MATCHES "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. 配置步骤 在工程根目录的CMakeLists.txt
添加 在对应的单测可执行文件添加的地方添加单测用例 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_GENERATOR
为MinGW Makefiles