Make是怎么工作的?
什么是makefile
一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为makefile就像一个Shell脚本一样,其中也可以执行操作系统的命令.
使用规则
目标(target) :依赖(prerequiries)…
<tab>命令(command)
<tab>命令(command)
目标文件 : 先决条件
命令
目标文件:可以是object file、可执行文件或者是一个标签
先决条件:生成目标文件所需的目标或文件
命令:要执行的命令
<tab>:每一个命令行必须以[Tab]字符开始,[Tab]字符告诉make此行是一个命令行,命令这一行需要tab键开头,否则编译器是无法识别的。此外,如果在makefile文件中的行尾加上空格键的话,也会导致make命令运行失败。
makefile的执行,是通过比较目标文件和先决条件这两部分文件的日期,如果先决条件文件日期比较新或者目标文件不存在,那么makefile就会去执行后续定义的命令
如何工作?
在默认的方式下,也就是我们只输入make命令。那么,
- make会在当前目录下找名字叫“Makefile”或“makefile”的文件。
八、Makefile常用的语法
为了增强Makefile的功能与增加程序的可移植性,Makefile中使用很多语法格式,如变量(宏)、通配符、函数、条件表达式、关键字等等。
①内部定义的宏
符号 | 含义 |
$ | 宏引用,如$(CC) |
$@ | 代表目标文件 |
$^ | 代表所有的依赖文件 |
$< | 代表第一个依赖文件 |
$? | 代表比目标的修改时间更晚的那些依赖文件。 |
$* | 代表去掉后缀的当前目标名。例如,若当前目标是pro.o,则$*表示pro |
宏的作用类似于C语言中的define。Makefile文件中的宏分为两种类:
系统内部定义的宏,上表已经列出,可以直接使用。
用户自己定义的宏,必须在makefile或命令行中明确定义。
格式1:宏标识符 = 值列表 //这个格式很少用
格式2:宏标识符 := 值列表 //这个使用正常
格式3:宏标识符 ?= 值列表
②关键符的含义
符号 | 含义 |
# | 注释以#为开头,至行尾结束。 |
\ | 将一个较长行使用反斜线(\)来分解为多行,反斜线之后不能有空格。 |
@ | 不要显示执行的指令。 |
- | 表示即使该行指令出错,也不会中断执行。 |
⑤目录与文件的搜索(详见范例讲解)
make只会在当前的目录中去找寻依赖文件和目标文件。但是,在大的工程中,许多的源文件分类存放在不同的目录中。关于目录与文件的搜索,Makefile 提供了两种方式:
第一种是设置全局访问路径VAPTH。
第二种是设置关键字vpath。
先介绍第一种方式:Makefile文件中有个特殊变量“VPATH”,如果定义了,则make就会在当前目录找不到的情况下,到所指定的目录中去找寻文件了。
语法:VPATH =src:../headers
上面的的定义指定两个目录,“src”和“../headers”,make会按照这个顺序进行搜索。目录由“冒号”分隔。
注意:定义了VPATH或vpath,这仅仅是对于makefile来说搜索目标和依赖文件的路径,但是对于命令行来说是无效的,也就是说在执行g++或者gcc时不会自动从VPATH 或者vpath中自动搜索要包含的头文件等信息文件。
加入“.PHONY clean”,是为了防止命名的重复,可以使用.PHONY来显式地指明一个伪目标,这样就不会对clean这个目标的性质定义产生混乱
make -j
在Linux编译程序的时候,IO往往不是瓶颈,那CPU就应该是一个影响编译速度的重要因素了。
用make -j带一个参数,可以把项目在进行并行编译,比如在一台双核的机器上,完全可以用make -j4,让make最多允许4个编译命令同时执行,这样可以更有效的利用CPU资源。意思是在同一时间可以进行并行编译的任务数
还是用Kernel来测试:
用make: 40分16秒
用make -j4:23分16秒
用make -j8:22分59秒
由此看来,在多核CPU上,适当的进行并行编译还是可以明显提高编译速度的。但并行的任务不宜太多,一般是以CPU的核心数目的两倍为宜。
#COMPILER_ROOT=$(PATH)
APP_NAME = neo_sx1278_ftm
#---------------------
# Include definition
#---------------------
LOCAL_INC_DIR = -I./ -I./platform/ -I./radio/
#---------------------
# Source code files
#---------------------
DIRS = $(shell find . -maxdepth 3 -type d)
LOCAL_SRC_FILES = $(foreach dir, $(DIRS), $(wildcard $(dir)/*.c))
LOCAL_OBJECTS = $(notdir $(patsubst %.c, %.o,$(LOCAL_SRC_FILES)))
#---------------------
# Library definition
#---------------------
STD_LIB_FILES = -lpthread -lm
USR_LIB= -L.
#---------------------
# Compilation options
#---------------------
$(APP_NAME) : $(LOCAL_SRC_FILES)
$(CC) $(LOCAL_CFLAGS) $(LOCAL_INC_DIR) $(USR_INC_DIR) $(LOCAL_SRC_FILES) $(STD_LIB_FILES) $(USR_LIB_FILES) -o $@
rm -rf *.o *.a
@echo ---- finished ----
.PHONY: clean
clean:
rm -rf $(APP_NAME)
rm -rf *.o *.a