makefile 工程管理---那些年我们一起学习linux程序设计
来源:互联网 发布:2016nba数据库统计 编辑:程序博客网 时间:2024/06/05 14:33
一.为什么使用 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 来删除目录下所以目标文件。
- makefile 工程管理---那些年我们一起学习linux程序设计
- makefile 工程管理---那些年我们一起学习linux程序设计
- shell 程序设计1--- 那些年我们一起学习linux程序设计
- shell 程序设计2--- 那些年我们一起学习linux程序设计
- shell 程序设计3--- 那些年我们一起学习linux程序设计
- 文件编程---那些年我们一起学习linux程序设计
- 时间编程---那些年我们一起学习linux程序设计
- 线程<一>---那些年我们一起学习linux程序设计 .
- 线程<二>---那些年我们一起学习linux程序设计
- 网络编程<一>---那些年我们一起学习linux程序设计 .
- 网络编程<二>---那些年我们一起学习linux程序设计
- linux 下的编译器和调试器---那些年我们一起学习linux程序设计
- 进程控制理论<一>---那些年我们一起学习linux程序设计
- 进程控制理论<二>---那些年我们一起学习linux程序设计
- 进程控制理论<三>---那些年我们一起学习linux程序设计
- 进程控制理论<四>---那些年我们一起学习linux程序设计 .
- 我们一起学习Makefile
- linux 应用程序设计基础--Makefile工程管理
- F - Piggy-Bank HDU 1114 (完全背包的变形+初始化细节)
- JAVASE生成一定范围的随机数
- How to create a PV image for XEN
- 在Android 中使用KSOAP2调用WebService
- 百度坐标转火星坐标
- makefile 工程管理---那些年我们一起学习linux程序设计
- 实用JS代码大全
- 关于函数strtok和strtok_r的使用要点和实现原理(一)
- 随机问题之--洗牌算法
- 【给移动应用开发新手的6个建议】
- 带精英策略的蚂蚁系统解决TSP问题matlab实现
- git常用命令
- linux 下的编译器和调试器
- 关于函数strtok和strtok_r的使用要点和实现原理(二)