开发工具——Makefile&CMake

开发工具专题之Makefile&CMake

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
目标…... : 依赖文件集合……
	命令 1      # 命令列表中的每条命令必须以 TAB 键开始,不能使用空格!
	命令 2
	……
例1:
main : main.o input.o calcu.o
	gcc -o main main.o input.o calcu.o
# 这条规则的目标是 main,main.o、input.o 和 calcu.o 是生成 main 的依赖文件

例2:
main: main.o input.o calcu.o
	gcc -o main main.o input.o calcu.o
main.o: main.c
	gcc -c main.c
input.o: input.c
	gcc -c input.c
calcu.o: calcu.c
	gcc -c calcu.c

clean:
 	rm *.o
 	rm main
# 首先更新第一条规则中的 main,第一条规则的目标成为默认目标,只要默认目标更新了那么就认为 Makefile 的工作。在第一次编译的时候由于 main 还不存在,因此第一条规则会执行,第一条规则依赖于文件 main.o、input.o 和 calcu.o 这个三个.o 文件,这三个.o 文件目前还都没有,因此必须先更新这三个文件。make 会查找以这三个.o 文件为目标的规则并执行。

例3:
main: main.o input.o calcu.o
	gcc -o main main.o input.o calcu.o
	
%.o : %.c         # 通配符
	gcc -c $<     # 自动化变量,依赖文件集合中的第一个文件,如果依赖文件是以模式(即“%”)定义的,那么“$<”就是符合模式的一系列				   的文件集合。
clean:
 	rm *.o
 	rm main

Makefile 变量

1
2
3
4
5
# Makefile 变量的使用
objects = main.o input.o  # objects为变量
objects += calcu.o        # 追加
main: $(objects)
	gcc -o main $(objects)

Makefile 函数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# 格式
$(函数名 参数集合)

# 1. 字符串替换
$(subst <from>,<to>,<text>)
例: $(subst zzk,ZZK,my name is zzk)    # 把字符串“my name is zzk”中的“zzk”替换为“ZZK”

# 2. 获取目录
$(dir <names…>)
例: $(dir </src/a.c>)   # 返回 “/src”

# 3. 提取文件名
$(notdir <names…>)

# 4. 完成循环
$(foreach <var>, <list>, <text>)     # 意思就是把参数<list>中的单词逐一取出来放到参数<var>中,然后再执行<text>所包含的表达式。每次<text>都会返回一个字符串,循环的过程中,<text>中所包含的每个字符串会以空格隔开,最后当整个循环结束时,<text>所返回的每个字符串所组成的整个字符串将会是函数 foreach 函数的返回值

CMake

源文件:CMakeLists.txt

CMake常用命令

 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
# 0
message("Hello World!")   # 打印"Hello World"
project(HELLO)   # 设置工程名称为 HELLO

# 1. add_executable 命令用于添加一个可执行程序目标,并设置目标所需的源文件
例:  add_executable(hello 1.c 2.c 3.c)   # 生成可执行文件 hello

# 2. add_library 命令用于添加一个库文件目标,并设置目标所需的源文件
例: add_library(mylib STATIC 1.c 2.c 3.c)  # 生成静态库文件 libmylib.a
	add_library(mylib SHARED 1.c 2.c 3.c)  # 生成动态库文件 libmylib.so

# 3. add_subdirectory 命令告诉 cmake 去指定的目录中寻找源码并执行它
例: add_subdirectory(src)   # 告诉 cmake 去 src 目录下寻找 CMakeLists.txt

# 4. aux_source_directory 命令会查找目录中的所有源文件
例:aux_source_directory(src SRC_LIST)  # 查找 src 目录下的所有源文件
    message("${SRC_LIST}") # 打印 SRC_LIST 变量

# 5. include_directories 命令用于设置头文件的搜索路径
include_directories(include)   # 头文件路径include

# 6. link_directories 命令用于设置库文件的搜索路径
# 	 link_libraries 命令会将指定库文件添加到链接库列表 
        例: link_directories(lib)
            link_libraries(${PROJECT_SOURCE_DIR}/lib/libhello.so)
# 7. target_include_directories 命令为指定目标设置头文件搜索路径
#	 target_link_libraries 命令为指定目标设置链接库文件
        例:target_link_libraries(hello-world PUBLIC libhello.so)
		   target_include_directories(hello-world PUBLIC libhello.so)
注:include_directories()link_libraries()是针对当前源码中的所有目标,并且还会向下传递(譬如通过
add_subdirectory 加载子源码时,也会将其传递给子源码)。在一个大的工程当中,这通常不规范、有时还
会编译出现错误、混乱,所以我们应尽量使用 target_include_directories() target_link_libraries(),保持整个
工程的目录清晰。

# 8. set 命令用于设置变量
例: set(SRC_LIST 1.c 2.c 3.c 4.c 5.c) #设置变量 VAR1=Hello
	message(${VAR1})

CMake常用变量

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
1. $(PROJECT_SOURCE_DIR)   # 工程顶层目录,也就是顶层 CMakeLists.txt 源码所在目录
2. $(PROJECT_BINARY_DIR)   # 工 程 BINARY_DIR , 也 就 是 顶 层 CMakeLists.txt 源码的BINARY_DIR
3. CMAKE_CURRENT_SOURCE_DIR   # 当前源码所在路径 
4. CMAKE_CURRENT_BINARY_DIR   # 当前源码的 BINARY_DIR

5. EXECUTABLE_OUTPUT_PATH	# 可执行程序的输出路径
6. LIBRARY_OUTPUT_PATH      # 库文件的输出路径
例: # 设置可执行文件和库文件输出路径
	set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
	set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)

CMake条件语句

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
if(expression)
 	# then section.
 	command1(args ...)
 	command2(args ...)
	...
elseif(expression2)
 	# elseif section.
 	command1(args ...)
	command2(args ...)
	...
else(expression)
 	# else section.
 	command1(args ...)
	command2(args ...)

CMake循环语句

 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
# foreach循环
foreach(loop_var arg1 arg2 ...)
 	command1(args ...)
 	command2(args ...)
	...
	endforeach(loop_var)
	
例:  # foreach 循环测试
set(my_list hello world china)
foreach(loop_var ${my_list})
 	message("${loop_var}")
endforeach()

foreach(loop_var RANGE 4)	# RANGE 关键字表示范围
 	message("${loop_var}")
endforeach()

set(my_list A B C D)
foreach(loop_var IN LISTS my_list)		# IN 关键字,循环列表
	message("${loop_var}")
endforeach()

# while...break 测试
set(loop_var 10)
while(loop_var GREATER 0) #loop_var>0 时 执行循环体
 	message("${loop_var}")
 	if(loop_var LESS 6) #当 loop_var 小于 6 时
 		message("break")
 		break() #跳出循环
 	endif()
 	math(EXPR loop_var "${loop_var} - 1")#loop_var--
endwhile()

CMake函数

通过 function()定义的函数它的使用范围是全局的,并不局限于当前 源码、可以在其子源码或者父源码中被使用

 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
# function 函数
function(<name> [arg1 [arg2 [arg3 ...]]])
 	command1(args ...)
 	command2(args ...)
 	...
endfunction(<name>)

例:# 函数名: xyz
function(xyz arg1 arg2)
 	message("${arg1} ${arg2}")
endfunction()
xyz(Hello World)	# 调用函数

# 函数名: xyz
function(xyz arg1 arg2)
 	message("ARGC: ${ARGC}")  # 传参数量
 	message("ARGV: ${ARGV}")  # 传参列表
 	message("ARGV0: ${ARGV0}")  # 第一个参数
 	# 循环打印出各个参数
 	set(i 0)
 	foreach(loop ${ARGV})
 		message("arg${i}: " ${loop})
 		math(EXPR i "${i} + 1")
 	endforeach()
endfunction()
# 调用函数
xyz(A B C D E F G)

CMake宏定义

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
macro(abc arg1 arg2)	# macro 宏
 	if(DEFINED ARGC)
 		message(true)
 	else()
 		message(false)
 	endif()
endmacro()

function(xyz arg1 arg2)		# function 函数
 	if(DEFINED ARGC)
 		message(true)
 	else()
 		message(false)
 	endif()
endfunction()

abc(A B C D)	# 调用宏
xyz(A B C D)	# 调用函数

# false
# true

CMake交叉编译

 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
# CMakeLists.txt  配置 ARM 交叉编译
cmake_minimum_required(VERSION 3.16.3)

set(CMAKE_SYSTEM_NAME Linux) #设置目标系统名字
set(CMAKE_SYSTEM_PROCESSOR arm) #设置目标处理器架构

# 指定编译器的 sysroot 路径
set(TOOLCHAIN_DIR /usr/local/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf)
set(CMAKE_SYSROOT ${TOOLCHAIN_DIR})
# 指定交叉编译器 arm-linux-gcc 和 arm-linux-g++
set(CMAKE_C_COMPILER ${TOOLCHAIN_DIR}/bin/arm-linux-gnueabihf-gcc)
set(CMAKE_CXX_COMPILER ${TOOLCHAIN_DIR}/bin/arm-linux-gnueabihf-g++)

# 为编译器添加编译选项
set(CMAKE_C_FLAGS "-march=armv7ve -mfpu=neon -mfloat-abi=hard -mcpu=cortex-a7")
set(CMAKE_CXX_FLAGS "-march=armv7ve -mfpu=neon -mfloat-abi=hard -mcpu=cortex-a7")
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
#################################
# end
##################################

project(HELLO) #设置工程名称
add_executable(main main.c)


# 编译选项 详见:https://blog.csdn.net/u013836909/article/details/107770465
-march=name  # 这指定了目标ARM体系结构的名称
comments powered by Disqus
Built with Hugo
Theme Stack designed by Jimmy