make命令和makefile文件的结合提供了一个在项目管理领域十分强大的工具.不仅被用于控制源代码的编译,而且还用于手册页的编写以及将应用程序安装到目标目录.
make命令
make命令的选项和参数
-k:
让make命令发现错误后继续执行-n:
输出要执行的步骤而不真正执行这些操作-f:
指定makefile文件
makefile的语法
makefile由依赖关系和规则构成.
依赖关系
定义最终应用程序里的每个文件与源文件间的关系.
规则写法:先写目标的名称,然后紧跟一个冒号,接着是空格或tab,最后是空格或tab隔开的文件列表.
例:
app: main.o 2.o 3.o
main.o: main.c a.h
2.o: 2.c a.h b.h
3.o: 3.c b.h c.h
如果想一次创建多个文件,可以利用伪目标all.假设应用程序由二进制文件myall和使用手册myapp.1组成.可以用下面这行语句来进行定义:
all: myapp myapp.1
如果未指定一个all目标,则make命令将只创建它在makefile中找到的第一个目标.
规则
规则所在的行必须以tab开头,用空格是不行的.此外,如果makefile文件中的某行以空格结尾,也可能导致make命令执行失败.
app: main.o 2.o 3.o
gcc -o app main.o 2.o 3.o
main.o: main.c a.h
gcc -c main.c
2.o: 2.c a.h b.h
gcc -c 2.c
3.o: 3.c b.h c.h
gcc -c 3.c
makefile中的宏
可以通过语句MACRONAME=value
在makefile文件中定义宏,引用宏的方法是使用$(MACRONAME)
或${MACRONAME}
. make的有些版本还接受$MACRONAME
的用法.如果想把一个宏的值设为空,可以用等号(=)后面留空.
例: 使用宏设置编译器和编译器选项
all: myapp
# Which compiler
CC = gcc
# Where are include files kept
INCLUDE = .
# Options for development
CFLAGS = -g -Wall -ansi
# Options for release
# CFLAGS = -O -Wall -ansi
myapp: main.o 2.o 3.o
$(CC) -o myapp main.o 2.o 3.o
main.o: main.c a.h
$(CC) -I$(INCLUDE) $(CFLAGS) -c main.c
2.o: 2.c a.h b.h
$(CC) -I$(INCLUDE) $(CFLAGS) -c 2.c
3.o: 3.c b.h c.h
$(CC) -I$(INCLUDE) $(CFLAGS) -c 3.c
make内置一些特殊的宏定义,在使用前展开,含义会随makefile文件的处理进展而发生变化.
宏 | 定义 |
---|---|
$? | 当前目标所依赖的文件列表中比当前目标文件还新的文件 |
$@ | 当前目标的名字 |
$< | 当前依赖文件的名字 |
$* | 不包括后缀名的当前依赖文件的名字 |
还有两个特殊字符,出现在命令之前:
-
告诉make忽略所有错误.如想创建一个目录,忽略目录已存在的错误,可以在mkdir命令前加一个减号@
告诉make在执行某条命令前不要将该命令显示在标准输出上.
多个目标
制作不止一个目标文件或者将多组命令集中到一个位置来执行是很有用的.
例:该例中新增了两个目标clean和install.make命令只能创建myapp, 需运行make clean和make install命令来创建目标clean和install.
all: myapp
# Which compiler
CC = gcc
# Where to install
INSTDIR = /usr/local/bin
# Where are include files kept
INCLUDE = .
# Options for development
CFLAGS = -g -Wall -ansi
# Options for release
# CFLAGS = -O -Wall -ansi
myapp: main.o 2.o 3.o
$(CC) -o myapp main.o 2.o 3.o
main.o: main.c a.h
$(CC) -I$(INCLUDE) $(CFLAGS) -c main.c
2.o: 2.c a.h b.h
$(CC) -I$(INCLUDE) $(CFLAGS) -c 2.c
3.o: 3.c b.h c.h
$(CC) -I$(INCLUDE) $(CFLAGS) -c 3.c
clean:
-rm main.o 2.o 3.o
install: myapp
@if [ -d $(INSTDIR) ]; \
then \
cp myapp $(INSTDIR);\
chmod a+x $(INSTDIR)/myapp;\
chmod og-w $(INSTDIR)/myapp;\
echo "Installed in $(INSTDIR)";\
else \
echo "Sorry, $(INSTDIR) does not exist";\
fi
管理函数库
例:本例通过穿件库函数mylib.a来创建目标myapp.例子中利用内置规则,将制作目标的规则去掉,只指定依赖关系,从而达到简化makefile文件的目的.
all: myapp
# Which compiler
CC = gcc
# Where to install
INSTDIR = /usr/local/bin
# Where are include files kept
INCLUDE = .
# Options for development
CFLAGS = -g -Wall -ansi
# Options for release
# CFLAGS = -O -Wall -ansi
# Local Libraries
MYLIB = mylib.a
myapp: main.o $(MYLIB)
$(CC) -o myapp main.o $(MYLIB)
$(MYLIB): $(MYLIB)(2.o) $(MYLIB)(3.o)
main.o: main.c a.h
2.o: 2.c a.h b.h
3.o: 3.c b.h c.h
clean:
-rm main.o 2.o 3.o $(MYLIB)
install: myapp
@if [ -d $(INSTDIR) ]; \
then \
cp myapp $(INSTDIR);\
chmod a+x $(INSTDIR)/myapp;\
chmod og-w $(INSTDIR)/myapp;\
echo "Installed in $(INSTDIR)";\
else \
echo "Sorry, $(INSTDIR) does not exist";\
fi
makefile文件和子目录
处理含有子目录的项目有两种方法
第一种是在子目录中编写第二makefile文件,用于编译该子目录下的源文件,并将其保存到一个函数库中,然后将该库文件复制到上一级的主目录中.主目录中的makefile文件包含一条用于制作函数库的规则,该规则会调用第二个makefile文件,如下所示:
mylib.a:
(cd mylibdirectory;$(MAKE))
由于make会针对每个命令调用一个新的shell,而第二个makefile文件的make命令本身又没有执行cd命令,但它又必须在一个不同的目录下创建函数库.为解决这一问题,用括号将两个命令括起来,从而确保它们只被一个单独的shell处理.
第二种方法是在原来的makefile文件中添加一些宏.新添加的宏通过在我们已见过的宏的尾部追加一个字母得到,字母D代表目录,字母F代表文件名.然后可以用下面的规则来替换内置的.c.o 后缀规则:
.c.o:
$(CC) $(CFLAGS) -c $(@D)/$(<F) -o $(@D)/$(@F)
作用:编译子目录中的源文件并将目标文件放在该子目录中.然后用如下依赖关系和规则来更新当前目录下的函数库:
mylib.a: mydir/2.o mydir/3.o
ar -rv mylib.a $?