书写规则
规则包括两部分,一个是依赖关系,一个是生成目标的方法.规则的语法:
targets : prerequisites
command
...
targets是文件名,以空格分开,可以使用通配符.一般来说,我们的目标基本上是一个文件,但也有可能是多个文件.
command是命令行,如果其不与"targets : prerequisites"在一行,那么必须以Tab键开头.
prerequisites也就是目标所依赖的文件(或依赖目标).如果其中的某个文件要比目标文件要新,那么,目标就被认为是"过时的",被认为是需要重生成的.
如果命令太长,你可以使用反斜杠\
作为换行符.马珂对一行上有多少个字符没有限制.规则告诉make两件事,文件的依赖关系和如何生成目标文件.
一般来说,make会以UNIX的标准Shell,也就是/bin/sh
来执行命令.
举一个很简单的例子:
当执行make指令的时候,结果如下:
D:\01Project\PA\demo>make
gcc main.c -o main
main.c
至于make的安装,这里不做介绍。
在规则中使用通配符
支持三个通配符:
*
:*.c
表示所有后缀为c的文件。~
:~
表示当前用户目录。?
:通常在依赖文件列表中使用,匹配所有有更新的目标。make在编译目标时,会去检查目标的依赖文件列表是否有文件更新,$?
表示当前依赖列表中已经更新的依赖文件。
clean:
rm -f *.o
objects = *.o
上述例子中表示了通配符同样可以使用在变量中。并不是说*.o
会展开,不!objects的值就是*.o
.Makefile中的变量其实就是C中的宏,如果你要让通配符在变量中展开,也就是让objects的值是所有.o
的文件名的集合,那么,你可以这样:
objects:=$(wildcard *.o)
另给一个变量使用通配符的例子:
1、列出一确定文件夹中的所有.c
文件。
objects := $(wildcard *.c)
来获取工作目录下的所有.c
文件列表
2、列出1中所有文件对应的.o
文件,在3中我们可以看到它是由make自动编译出的:
$(patsubst %.c,%.o,$(wildcard *.c))
首先使用wildcard函数获取工作目录下的.c
文件列表,之后将列表中的所有文件名的后缀.c
替换为.o
。这样我们就可以得到在当前目录可生成的.o
文件列表。
3、由1,2两步,可写出编译并链接所有.c
和.o
文件
objects := $(patsubst %.c,%.o,$(wildcard *.c))
foo : $(objects)
cc -o foo $(objects)
把Makefile修改为如下:
main: main.c
gcc main.c -o main
@echo $?
那么执行make指令的时候结果如下:
D:\01Project\PA\demo>make
gcc main.c -o main
main.c
在目标文件没有被生成的时候,make工具会将所有的依赖文件视为已更新的文件,从而重新编译生成目标。
文件搜寻
在一些大的工程中,有大量的源文件,我们通常的做法是把这许多的源文件分类,并存放在不同的目录中。所以,当make需要去找寻文件的依赖关系时,你可以在文件前加上路径,但最好的办法是把一个路径告诉make,让make再自动去找。
Makefile文件中的特殊变量VPATH
就是完成这个功能的,如果没有指明这个变量,make只会在当前的目录中去找寻依赖文件和目标文件。如果定义了这个变量,那么,make就会在当前目录找不到的情况下,到所指定的目录中去找寻文件了。
VPATH=src:../headers
上面的定义指定了两个目录,src
和../headers
,make会按照这个顺序进行搜索。目录由:
冒号分割。
另一个设置文件搜索路径的方法是使用make的vpath
关键字,这不是变量,这是一个make的关键字。
vpath %.h ../headers
该语句表示,要求make在../headers
目录下搜索所有以.h
结尾的文件。
伪目标
我们提到过一个clean
的目标,这是一个"伪目标"。
clean:
rm *.o temp
为了避免和文件重名的情况,我们可以使用搞一个特殊的标记.PHONY
来显示地指明一个目标就是“伪目标”。
.PHONY : clean
clean :
rm *.o temp
特殊变量
在makefile的编译规则中,有一些特殊的内建变量,下面就列出一些常用的内建变量。
$@
:表示需要被编译的目标
$<
:依赖列表中第一个依赖文件名
$^
:依赖列表中所有文件
$?
:依赖文件列表中所有有更新的文件,以空格分割
~或者./
:用户目录。
AR
:静态库打包命令的名字,缺省值时ar
。
ARFLAGS
:静态库打包命令的选项,缺省值时rv
。
AS
:汇编器的名称,缺省值是as
。
ASFLAGS
:汇编器的选项,没有定义
CC
:C编译器的名称,缺省值是cc
。
CFLAGS
:C编译器的选项,没有定义。
CXX
:C++编译器的名称,缺省值是g++
。
CXXFLAGS
:C++编译器的选项,没有定义。
CPP
:C预处理的名字,缺省值是$(CC) -E
。
CPPFLAGS
:C预处理器的选项,没有定义。
LD
:链接器的名字,缺省值是ld
。
LDFLAGS
:链接器的选项,没有定义。
TARGET_ARCH
:和目标平台相关的命令行选项,没有定义。
OUTPUT_OPTION
:输出的命令行选项,缺省值是-o $@
。
LINK.o
:把.o
文件链接在一起的命令行,缺省值是$(CC) $(LDFLAGS) $(TARGET_ARCH)
。
COMPILE.c
:编译.c
文件的命令行,缺省值是$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c
。
RM
:删除命令的名字,缺省值是rm -f
。
make的隐含规则数据库中用到了很多变量,有些变量没有定义(例如CFLAGS
),有些变量定义了缺省值(例如CC
),我们写Makefile时可以重新定义这些变量的值,也可以在缺省值的基础上追加。
模式规则
模式规则类似于普通规则。只是在模式规则中,目标名中需要包含有模式字符%
,包含有模式字符%
的目标被用来匹配一个文件名,%
可以匹配任何非空字符串。下列示例就是一个makefile内建的模式规则,由所有的.c文件生成对应的.o文件:
%.o : %.c
$(CC) -c $(CFLAGS) $< -o $@
示例
#指定编译器和链接器
CC=gcc
LD=gcc
#-I头文件目录 -l库名 -L 库路径
#指定头文件目录
#指定库文件路径 和 库文件
INCLUDE = -I./ -I./include
LIB = -L./lib -lhello
BUILD_PATH := ./build
TARGET_NAME := main
TARGET := $(BUILD_PATH)/$(TARGET_NAME)
#当前目录所有.c文件
SRCS := $(wildcard *.c)
#Build目录下所有的.o文件
OBJS := $(patsubst %.c, $(BUILD_PATH)/%.o, $(SRCS))
.PHONY : all clean
all: $(TARGET)
#要生成可执行文件需要依赖.o文件
#将所有目标文件和库文件链接即可生成可执行文件
$(TARGET): $(OBJS)
$(LD) $^ $(LIB) -o $@
#.o文件由.c编译而来
#编译每一个.o文件
$(BUILD_PATH)/%.o: %.c
$(CC) -c $< $(INCLUDE) -o $@
#make clean命令 清空生成的文件
clean:
rm -rf $(OBJS) $(TARGET)
多目录源文件通用Makefile示例
PWD := ${shell pwd}
CC = ${CROSS_COMPILE}gcc
LD = ${CROSS_COMPILE}gcc
BUILD_PATH := ./build
TARGET_NAME := audio_demo.bin
TARGET := ${BUILD_PATH}/bin/${TARGET_NAME}
#all source code path,can add more....
SRC_PATH:=./ ./subdir1 ./subdir2
${info "SRC_PATH:${SRC_PATH}"}
#all *.c file
SRCS:=${foreach dir,${SRC_PATH},${wildcard ${dir}/*.c}}
${info "SRCS:${SRCS}"}
#all *.o file
OBJS := ${patsubst %.c, ${BUILD_PATH}/%.o, ${notdir ${SRCS}}}
${info "OBJS:${OBJS}"}
#add source code search path
VPATH=${SRC_PATH}
${info "VPATH:${VPATH}"}
#-I header file directory,can add more
INCLUDE = ${addprefix -I,${SRC_PATH}} -I./include
${info "INCLUDE:${INCLUDE}"}
#-l libName -L lib directory,can add more
LIB = -L./lib -lasound -lm -pthread \
# -L${CROSS_TOOL_PATH}/aarch64-buildroot-linux-gnu/sysroot/usr/lib -lasound -lm -pthread
CFLAGS = -g -Wall -O2 -std=gnu11
.PHONY : all clean
all:build_prepare ${TARGET}
${TARGET}: ${OBJS}
${LD} -o $@ $^ ${LIB}
${BUILD_PATH}/%.o: %.c
${CC} -c $^ -o $@ ${INCLUDE} ${CFLAGS}
#mkdir
build_prepare:
if [ ! -e ${BUILD_PATH}/bin ]; then \
mkdir ${BUILD_PATH};\
mkdir ${BUILD_PATH}/bin;\
fi
clean:
rm -rf ${OBJS} ${TARGET}