通过Makefile管理。
根据目标和依赖的最后修改时间判断是否需要更新。
三要素:目标、依赖和规则。
目标可以是文件或者伪目标。
命令可以与目标依赖同一行,中间用;隔开。或者独立一行,一条命令占一行,命令以tab开始。
可以使用反斜杠\续行,但是其后不能有空格。
make有默认规则,.c文件会默认生成同名的.o文件。所以单独每个文件只需要将目标和除文件本身外的其他依赖写上即可。
单目标,多依赖。
命令前加-,表示忽略命令执行错误。
可以通过-f或者--file=指定make读取的文件。
使用include包含其他Makefile文件,多个文件使用空格分隔,可以使用通配符。
include前通常会有-,例如-include xxx xxx,这表示忽略包含文件不存在或者无法创建的错误提示。
不加-,编译停止
加-,可以编译。
$有特殊含义,表示变量或函数的引用。
order-only依赖使用|指示,|后边的为此依赖,表示构建目标存在时,此依赖改变也不会重建目标。
库文件通过lNAME的方式作为依赖。只是指定了库的名称,最后对应的库应该是libNAME.a或者libNAME.so的文件。
通配符
*、?、[]可以用在目标、规则命令中,其他地方需要wildcard函数指定。注意在文件名中出现通配符的话需要\转义。
变量中使用wildcard显示指明通配符,他被展开为已经存在的、使用空格分开的、匹配此模式的所有文件的列表。objs=$(wildcard *.o),这样objs的值是具体的一个一个的xxx.o文件,也就是通配符展开。如果直接objs=*.o那么objs就是*.o这个字符串。
objs:=$(patsubst %.c,%.o,$(wildcard *.c)):获取所有.c文件,并将后缀改为.o,构成.o列表赋值给变量。
$^表示通过目录搜索得到的依赖文件的完整路径名列表,即所有的依赖文件。
$@表示规则的目标。
$<第一个依赖文件
$?表示所有依赖文件
变量
多字符变量的引用必须加()。定义变量有两种方式:直接展开(:=)和递归展开(=或define),推荐使用前者。
?=表示条件赋值,如果左值没有赋值则将右值赋值。
VPATH:指定搜索路径。路径之间用冒号隔开。
VPATH=src:../headers
GPATH:与VPATH类似,保证已存在的目标在所在目录重建。
CFLAGS:
.LIBPATTERNS:其值为lib%.so lib%.a,使用lNAME时会将%替换为NAME。
RM:定义为RM=rm -f,可以使用$(RM)来作为clean的命令
MAKELEVEL:make的调用深度。
关键字
vpath:指定文件的搜索路径
vpath PATTERN DIR 指定
vpath PATTERN 清除指定
vpath 清除所有
PATTERN需要使用%作为模式匹配,匹配一个或者多个字符。
伪目标
.PHONY是一个特殊目标,其依赖就是伪目标,
可以使用all作为伪目标来构建多个程序,这些程序就是all的依赖。
强制目标,指没有依赖也没有命令,且目标不存在,主要用在非GNU的make版本中。
特殊目标
.PHONY .SUFFIXES .DEFAULT .PRECIOUS .INTERMEDIATE .SECONDARY .DELETE_ON_ERROR .IGNORE .LOW_RESOLUTION_TIME .SILENT .EXPORT_ALL_VARIABLES .NOTPARALLEL
通过例子讲解
#下面三行定义变量ROOT、SRC_DIR、BUILD_DIR并赋值,分别记录了根目录、源码目录和build中间目录
ROOT=.
SRC_DIR=$(ROOT)/src
BUILD_DIR = build
#下面两行CC表示编译器,CFLAGS表示编译的参数
CC = gcc
CFLAGS=-ansi -I$(ROOT)/include -Wall -DLINUX -D_GNU_SOURCE
#下面两行LIBAPUE表示库的路径,LDLIBS表示链接的参数-L表示库路径-l表示库名称
LIBAPUE=$(ROOT)/lib/libapue.a
LDLIBS=-L$(ROOT)/lib -lapue
#下面两行C_FILES表示c源文件,wildcard指明后边的*表示通配符,否则*不起作用。
#OBJ_FILES是c文件对应的_c.o文件,冒号前边表示文件列表,后边表示匹配规则。%与*作用类似。
C_FILES = $(wildcard $(SRC_DIR)/*.c)
OBJ_FILES = $(C_FILES:$(SRC_DIR)/%.c=$(BUILD_DIR)/%_c.o)
#all后边表示我们最终想要得到的文件,all是一个伪目标。
all: linux-api-demo
#表示目标linux-api-demo的依赖和目标已经产生的方法
linux-api-demo:$(OBJ_FILES) $(LIBAPUE)
gcc -o linux-api-demo $(OBJ_FILES) $(LDLIBS)
表示.c生成.o目标的规则,$<表示依赖,$@表示目标,$(@D)表示目标的路径,不包括/
$(BUILD_DIR)/%_c.o: $(SRC_DIR)/%.c
mkdir -p $(@D)
$(CC) $(CFLAGS) $< -c -o $@
#清除规则,clean伪目标
clean:
rm -rf $(BUILD_DIR) linux-api-demo
#make执行的时候先通过 all查看其依赖,all的依赖就是我们要得目标,此处是linux-api-demo,
#要得到linux-api-demo,就要找其生成规则,找到后发现他的依赖是.o和.a文件,
#继续找.o文件的生成规则,找到后根据规则命令生成对应的文件,此处文件的生成是一个一个的,不是批量
#的,可以通过make的输出看出这一点。
#.o和.a生成后满足了生成linux-api-demo的条件,编译产生linux-api-demo后all的依赖也满足了,
#此时make完成。