Makefile中的注释
makefile文件中的注释以#开始,并且直到本行结束。正如在C源文件中一样,makefile文件中的注释有助于作者也其他人更好的理解文件编写时所期望的作用。
Makefile中的宏
即使有make与makefile是管理多个源码文件工程的强大工具。然而,对于由大量的文件所组成的工程来说,他们仍显得庞大和不灵活。所以Makefile允许我们使用宏,从而我们可以将其写为更为通用的形式。
我
们在makefile文件中以MACRONAME=value的形式定义一个宏,然后以$(MACRONAME)或是${MACRONAME}的形式来访
问MACRONAME的值。一些版本的make也可以接受$MACRONAME的形式。我们也可以通过将=之后的部分留空来设置一个空的宏。
在makefile文件中,宏通常用于编译的选项。通常,当正在开发一个程序时,我们通常并不会使用优化选项来进行编译,但是却需要包含调试信息。而对于发布一个程序通常是相反的情况:发布一个运行速度尽量快而不带任何调试信息的二进制程序。
Makefile1
的另一个问题是他假定编译器为gcc。在其他的Unix系统上,我们也许会使用cc或是c89。如果我们希望将我们的makefile运行在其他版本的
Unix系统上,或是如果我们在我们的系统上使用一个不同的编译器,我们就需要修改makefile的多行来使其工作。宏是用于收集系统相关部分的好方
法,从而可以很容易的进行修改。
宏通常是在makefile文件本身内部定义的,但是也可以通过带有宏定义的make命令来指定,例如
make
CC=c89。类似这样的命令行定义可以覆盖makefile文件中的定义。当在makefile文件外部使用时,宏定义必须作为一个单独的参数进行传
递,所以要避免空格或是使用引号的形式:make "CC = c89"。
试验--带有宏的Makefile
下面是我们的makefile的一个修正版本,Makefile2,其中使用一些宏:
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
如果我们删除我们原有的安装,而使用这个新的makefile来创建一个新的安装,我们会得到下面的信息:
$ rm *.o myapp
$ make -f Makefile2
gcc -I. -g -Wall -ansi -c main.c
gcc -I. -g -Wall -ansi -c 2.c
gcc -I. -g -Wall -ansi -c 3.c
gcc -o myapp main.o 2.o 3.o
$
工作原理
make程序使用合适的定义来替换$(CC),$(CFLAGS)以及$(INCLUDE),与带有#define形式的C编译器类似。现在如是我们需要修改编译命令,我们只需要修改makefile文件中的一行。
事
实上,make有多个内部宏,从而我们可以更为简洁的来使用。在下表中,我们列出其中最常用的一些;我们会在后的例子中看到他们的使用。这些宏中的每一个
仅会在他们刚要被使用时进行扩展,所以宏的语义会随着makefile文件的处理而变化。事实上,如果这些不以这样的方进行工作,他们就不会太大的作用。
$? 距离当前目标最近一次修改的需求列表
$@ 当前目标的名字
$< 当前需求的名字
$* 不带前缀的当前需求的名字
还有另外两个有用的特殊字符,也许我们会在makefile文件中的命令前遇到:
-选择make忽略所有错误。例如,如果我们希望创建一个目录,但是希望忽略错误,也许是因为这个目录已经存在了,我们只需要在mkdir命令之前带有一个负号。我们将会在后面的章节中看到-的使用。
@告诉make在执行命令之前不要将命令输出到标准输出。如果我们希望使用echo来显示一些指令时,这个字符就特别有用。
多个目标
通
常需要构建多个目标文件,而不是一个目标文件,或者是在一个地方收集多个命令组。我们可以扩展我们的makefile文件来完成这些任务。下面我们添加一
个"clean"选项来移除不需要的目标文件,以及一个"install"选项来将完成的程序移动到另一个不同的目录中。
试验--多个目标
下面是下一个版本的makefile,Makefile3。
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
在这个makefile中需要注意多个地方。首先,特殊目标all仍然只是指定myapp作为目标。所以,当我们执行make命令而没有指定一个目标时,默认的行为就是构建目标myapp。
接
下来需要特别注意的另外两个目标,clean与install。clean目标使用rm命令来移除目标文件。这个命令以-开头,这会使得make命令忽略
命令的结果,所以make
clean总会成功,即使并没有目标文件而rm命令返回一个错误。目标"clean"并没有为clean指定任何所依赖的条件;clean之后的行是空
的。所以这个目标总是被认为是最新的,并且如果clean被指定为一个目标,那么其规则总是被执行。
install目标依赖于
myapp,所以make会知道在执行其他命令运行install之前必须先创建myapp。install的规则由一些shell脚本命令所组成。因为
make会为执行规则而调用shell,并且第一条规则使用一个新的shell,所以我们必须添加反斜线,这样所有的脚本命令就全在一个逻辑行,并且会全
部传递给一个单独的shell调用。这个命令以@开头,这会通知make命令在执行规则之前不要在标准输出上打印出命令。
install目标顺序执行命令将程序安装在其最终位置上。他在执行下一条命令之前并不会检测前一条命令是否执行成功。如果只有前一条命令成功执行之后才可以执行后续的命令,那么我们必须使用&&符号将其联合,如下所示:
@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” ; false ; \
fi
也许我们会回想起第2章的内容,这就是一个shell "and"命令,而且其效果是只有前一条命令执行成功之后才会执行后续的命令。在这里我们并不会关心是否保证前一条命令执行成功,所以我们只是使用这种较为简单的形式。
也许我们作为一个普通用户并不具有在/usr/local/bin目录下安装程序的权限。我们可以修改makefile文件从而使用另一个不同的安装目录,或是改变这个目录的权限,或者是在执行make install之前切换到root用户。
$ rm *.o myapp
$ make -f Makefile3
gcc -I. -g -Wall -ansi -c main.c
gcc -I. -g -Wall -ansi -c 2.c
gcc -I. -g -Wall -ansi -c 3.c
gcc -o myapp main.o 2.o 3.o
$ make -f Makefile3
make: Nothing to be done for ‘all’.
$ rm myapp
$ make -f Makefile3 install
gcc -o myapp main.o 2.o 3.o
Installed in /usr/local/bin
$ make -f Makefile3 clean
rm main.o 2.o 3.o
$
工作原理
首
先,我们删除myapp与所有的目标文件。make命令会使用目标all,从而构建myapp。接下来我们再次运行make玲,因为myapp已经是最新
的了,所以make不会做任何事情。然后我们删除myapp并且运行make
install。这会重新构建这个二进制文件,并且将其拷贝到安装目录。最后我们运行make clean,这会删除目标文件。
阅读(552) | 评论(0) | 转发(0) |