makefile 工程管理---那些年我们一起学习linux程序设计
一.为什么使用 makeLinux 程序员必须学会使用GNU make 来构建和管理自己的软件工程。GNU的make能够使整个软件工程的编译、链接只需要一个命令就可以完成。make工具可以解决重新编译所受改动影响的源文件带来的问题。 二.makefileMake在执行时,需要一个命名为Makefile的文件。Makefile文件描述了整个工程的编译,连接等规则。其中包括:1.工
一.为什么使用 make
Linux 程序员必须学会使用GNU make 来构建和管理自己的软件工程。GNU的make能够使整个软件工程的编译、链接只需要一个命令就可以完成。
make工具可以解决重新编译所受改动影响的源文件带来的问题。
二.makefile
Make在执行时,需要一个命名为Makefile的文件。Makefile文件描述了整个工程的编译,连接等规则。
其中包括:1.工程中的哪些源文件需要编译以及如何编译;
2需要创建哪些库文件以及如何创建这些库文件、
3如何最后产生我们想要的可执行文件。
三.makefile的语法
文件名默认应该为:
GNUmakefile makefile 或者Makefile然后执行make命令进行自动执行文件,并定义了源文件的依赖关系(当同一个目录下同时有这三个文件,则按照GNUmakefile makefile Makefile顺序优先执行)
Makefile(术语)
规则:用于说明如何生成一个或者多个目标文件,规则格式如下:
目标:依赖文件
(以tab空格)命令
例如:
Main.o :main.c
gcc –c main.c (命令是的到目标的命令)
目标:
在Makefile中,规则的顺序是很重要的,因为,Makefile中只应该有一个最终目标,其他的目标都是被这个目标所连带出来的,所以一定要让make知道你的最终目标是什么。
一般来说,定义在Makefile中的目标可能会有很多,但是第一条规则中的目标被确立为最终目标。
文件名:
Make命令默认在当前目录下寻找名字为makefile或者Makefile的工程文件,当名字不为这两者之一时,可以使用如下方法指定:
make -f 文件名
伪目标:
Makefile 中把那些没有任何依赖只有执行动作的目标称为“伪目标”(phony targets).
.PHONY : clean
clean :
rm -f hello main.o fun1.o fun2.o
“.PHONY”将“clean”目标声称为伪目标
变量:
在makefile 中,存在系统默认的自动变量
(1) $^ :代表所有的依赖文件
(2) $@ :代表目标
(3) $< :代表第一个依赖文件
例:
hello : main.o fun1.o fun2.o
gcc main.o fun1.o fun2.o -o hello
等效于:hello : main.o fun1.o fun2.o
gcc $^ -o $@
杂项:
总结:要注意的几点: 1.每一个命令行必须以[Tab]字符开始,[Tab]字符告诉make此行是一个命令行。make按照命令完成相应工作 2.Makefile中“#”字符后的内容被视作注释。 3.Hello :hello.c @gcc hello.c -o hello @:取消回显 (不显示执行的命令:gcc hello.c -o hello)
|
四.makefile 文件中的宏
makefile文件中的宏常被用于设置编译器的选项。
在软件的开发阶段:我们一般不对编译结果进行优化,而是将调试信息包含进去。
在软件的发行版:往往跟开发阶段相反,即编译结果为不包含调试信息的容量较小的二进制可执行文件,使其执行速度尽可能的快。
makefile 文件的另一个问题是,假设编译器的名字为gcc,而其他系统的UNIX系统的编译器的名字为cc 或者c89.如果想将makefile文件移植到另一版本的UNIX系统中,或在现有系统中使用另一个编译器,为了使其工作,我们不得不修改makefile文件中许多行内容。而宏用来收集所有这些与系统相关内容的好方法,通过使用宏定义,可以方便地修改这些内容。
宏通常都是在makefile文件内部定义的,但也可以在调用make命令时在命令行上给出宏定义,例如:make CC=c89 命令行上的宏定义将替换在makefile文件中的宏定义。当在makefile文件之外使用宏定义时,要注意宏定义必须以单个参数的形式传递,要避免在宏定义中使用空格或引号,下例是错误的:make "CC=c89" |
实验:带宏定义的makfile文件
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
我们删除旧的安装文件及中间文件*.o文件,并通过新的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)替换为相应的定义,这与C语言编译器对#define语句的处理方式一样。现在,想改变编译器命令,我们只需修改makefile文件找那个的一行即可。CC=XXX
make内置了一些特殊的宏定义,是makefile文件更加简洁。下面是几个常见的宏,这些宏在使用前才展开
宏 | 说明 |
$? $@ $< $* |
列出当前目标所依赖的文件列表中比当前目标文件还要新的文件 当前目标的名字 当前依赖文件的名字 不包括后缀名的当前所依赖的文件名字
|
五.多个目标
通常我们所需的目标文件不只一个,或者我们希望将多个命令集中到一个位置来执行,通过扩展makfile文件即可达到这一目的。下面我们在makfile文件中增加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
这个makefile文件中有几处需要注意。首先,特殊目标all仍然只定义了myapp这一目标。因此,如果在执行make命令时未指定目标,它的默认行为就是创建目标myapp.
另外一个值得关注的地方是两个新增加的目标:clean和install .目标clean用rm命令来删除目标文件。rm命令以减号-开头,减号的含义是让make命令忽略rm命令执行的结果,这意味着即使由于目标文件不存在而导致rm命令返回错误,执行命令make clean 也会成功。
目标install依赖于myapp,所以make命令知道它必须首先创建myapp,然后才能执行制作该目标所需要的其他命令。用于制作install目标的规则有几个shell脚本命令组成。由于make命令在执行规则时会调用一个shell,并且会针对每个命令使用一个新的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";\
fi
&&就是“与”的意思,即每个后续命令只在前面的命令都执行成功的前提下才会被执行。
你可能不能以普通用户的身份将新命令安装到目录/usr/local/bin下,在执行命令make install之前,可以修改makefile文件以选择另一安装目录,或是改变该目录的权限,或是通过命令su切换用户身份到超级用户root
# rm *.o myapp
# make -f makefile
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 makefile
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,并创建可执行程序mapp.然后再次运行make命令,但因为myapp已经是最新的,所以make命令未作任何事。接下来,删除myapp并执行命令make install, 它重新创建二进制文件myapp并将其拷贝到安装目录中。最后,运行命令make clean 来删除目录下所以目标文件。
更多推荐
所有评论(0)