当前位置:   article > 正文

make命令与makefile文件_make -f

make -f

转载自:http://blog.csdn.net/lihao21/article/details/68137648  博主:haozlee

一、多个源文件带来的问题

在编写c/c++测试程序时,我们习惯每次修改一处代码,然后就马上编译运行来查看运行的结果。这种编译方式对于小程序来说是没有多大问题的,可对于大型程序来说,由于包含了大量的源文件,如果每次改动一个地方都需要编译所有的源文件,这个简单的直接编译所有源文件方式对程序员来说简直是噩耗。 
我们看一个例子:

  1. // main.c
  2. #include "a.h"
  3. // 2.c
  4. #include "a.h"
  5. #include "b.h"
  6. // 3.c
  7. #include "b.h"
  8. #include "c.h"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

如果程序员只修改了头文件c.h,则源文件main.c2.c都无需编译,因为它们不依赖这个头文件。而对3.c来说,由于它包含了c.h,所以在头文件c.h改动后,就必须得新编译。 
而如果改动了b.h可是忘记编译了2.c,那么最终的程序就可能无法正常工作。 
make 工具就是为了解决上述问题而出现的,它会在必要时重新编译所有受改动影响的源文件。

二、make 命令

make命令本身支持许多选项,最常用的是-f选项。如果我们直接运行

make

那么make命令会首先在当前目录查找名为makefile的文件,如果找不到,就会查找名为Makefile的文件。 
为了指示make命令将哪个文件作为makefile文件,可以使用 -f 选项:

make -f Makefile1

三、makefile 文件

上面提到makefile文件,那么什么是makefile文件呢? 
make命令功能虽然十分强大,但是光凭其自身无法了解如何构建应用程序的。这时,makefile就出来了,它告诉make应用程序如何构建的。make命令和makefile文件的结合提供了一个在管理项目的十分强大的工具,它们不仅用于控制源文件的编译,而且还提供了将应用程序安装到目标目录等其他功能。

3.1 依赖关系

依赖关系定义了应用程序里面每个文件与其他源文件之间的关系。例如在上面的例子中,我们可以定义最终应用程序依赖于目标文件main.o2.o3.o。同样,main.o依赖于main.ca.h2.o依赖于2.ca.hb.h3.o依赖于3.cb.hc.h。 
在makefile文件中,依赖关系的写法是:先写目标的名称,然后紧跟一个冒号,接着是空格或者制表符tab,最后是用空格或者制表符tab隔开的文件列表。上面的例子的依赖关系如下:

  1. myapp: main.o 2.o 3.o
  2. main.o: main.c a.h
  3. 2.o: 2.c a.h b.h
  4. 3.o: 3.c b.h c.h
  • 1
  • 2
  • 3
  • 4

这组依赖关系形成一个层次结构,展示了源文件之间的关系。例如,如果源文件b.h发生改变,就需要重新编译2.o3.o,接下来还需要重新编译myapp

3.2 规则

makefiel文件中的规则定义了目标的创建方式。在上面的例子中,我们使用gcc -c 2.c创建2.o。这个gcc命令即是目标2.o的创建方式,也即是规则。 
在makefile文件中,规则都必须以tab开头。 
在源文件所在的目录下创建Makefile1文件,其内容如下。

  1. myapp: main.o 2.o 3.o
  2. gcc -o myapp main.o 2.o 3.o
  3. main.o: main.c a.h
  4. gcc -c main.c
  5. 2.o: 2.c a.h b.h
  6. gcc -c 2.c
  7. 3.o: 3.c b.h c.h
  8. gcc -c 3.c
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

三个头文件a.hb.hc.h内容都为空,源文件的内容如下:

  1. /* main.c */
  2. #include <stdlib.h>
  3. #include "a.h"
  4. extern void function_two();
  5. extern void function_three();
  6. int main()
  7. {
  8. function_two();
  9. function_three();
  10. exit(EXIT_SUCCESS);
  11. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  1. /* 2.c */
  2. #include <stdio.h>
  3. #include "a.h"
  4. #include "b.h"
  5. void function_two() {
  6. printf("function two\n");
  7. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  1. /* 3.c */
  2. #include <stdio.h>
  3. #include "b.h"
  4. #include "c.h"
  5. void function_three() {
  6. printf("function three\n");
  7. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

执行make命令,:

$ make -f Makefile1 
gcc -c main.c 
gcc -c 2.c 
gcc -c 3.c 
gcc -o myapp main.o 2.o 3.o

运行应用程序:

$ ./myapp 
function two 
function three

从输出可以说明应用程序已被正确构建。

如果改变b.h头文件,makefile能够正确处理这一变化,只有2.c3.c发生重新编译:

$ touch b.h

$ make -f Makefile1 
gcc -c 2.c 
gcc -c 3.c 
gcc -o myapp main.o 2.o 3.o

3.3 注释

makefile文件使用#来表示注释,一直延续到这一行的结束。

3.4 宏

不同的平台下可能使用不同的编译器,不同的环境(例如开发与线上环境)也可能使用不同的编译器选项,为了便于修改makefile这些可变的参数,我们可以使用宏来实现makefile。 
makefile引用宏定义的方法为$(MACRONAME)。我们来看如何使用宏来改写上面的makefile文件。

  1. all: myapp
  2. # 编译器
  3. CC = gcc
  4. # include的搜索路径
  5. INCLUDE = .
  6. # 编译器参数
  7. CFLAGS = -g -Wall -ansi
  8. myapp: main.o 2.o 3.o
  9. $(CC) -o myapp main.o 2.o 3.o
  10. main.o: main.c a.h
  11. $(CC) -I$(INCLUDE) $(CFLAGS) -c main.c
  12. 2.o: 2.c a.h b.h
  13. $(CC) -I$(INCLUDE) $(CFLAGS) -c 2.c
  14. 3.o: 3.c b.h c.h
  15. $(CC) -I$(INCLUDE) $(CFLAGS) -c 3.c
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

我们习惯在makefile文件中将第一个目标定义为all,然后再列出其他从属的目标,上面的makefile也遵循这个约定。

运行make命令:

$ 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

同样也正确构建了应用程序myapp

3.5 多个目标

makefile文件除了定义编译的目标外,还可以定义其他的目标。例如,增加一个clean选项来删除不需要的目标文件,增加一个install选项来将编译成功的应用程序安装到另一个目录下,等等。

  1. all: myapp
  2. CC = gcc
  3. INSTDIR = /usr/local/bin
  4. INCLUDE = .
  5. CFLAGS = -g -Wall -ansi
  6. myapp: main.o 2.o 3.o
  7. $(CC) -o myapp main.o 2.o 3.o
  8. main.o: main.c a.h
  9. $(CC) -I$(INCLUDE) $(CFLAGS) -c main.c
  10. 2.o: 2.c a.h b.h
  11. $(CC) -I$(INCLUDE) $(CFLAGS) -c 2.c
  12. 3.o: 3.c b.h c.h
  13. $(CC) -I$(INCLUDE) $(CFLAGS) -c 3.c
  14. clean:
  15. -rm main.o 2.o 3.o
  16. install: myapp
  17. @if [ -d $(INSTDIR) ]; \
  18. then \
  19. cp myapp $(INSTDIR);\
  20. chmod a+x $(INSTDIR)/myapp;\
  21. chmod og-w $(INSTDIR)/myapp;\
  22. echo "Install in $(INSTDIR)";\
  23. else \
  24. echo "sorry, $(INSTDIR) does not exist";\
  25. fi
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

上面的makefile文件有几点需要注意的。 
(1)特殊目标all只指定了myapp这个目标,因此,在执行make命令时未指定目标,它的默认行为就是创建目标myapp。 
(2)目标clean用来测试编译过程中产生的中间文件。 
(3)目标install用于将应用程序安装到指定目录,它依赖于myapp,即执行install前须先创建myappinstall目标由shell脚本组成,由于make命令在执行规则时会调用一个shell,并且会针对每个规则使用一个新的shell,所以必须在上面每行代码的结尾加上一个\,让所有的shell脚本都处于同一行。 
脚本以@开头,说明make在执行这些规则之前不会在标准输出显示命令本身。

创建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

myapp安装到指到目录:

$ make -f Makefile3 install 
Install in /usr/local/bin

然后可以直接执行myapp:

$ myapp 
function two 
function three

删除中间文件:

$ make -f Makefile3 clean 
rm main.o 2.o 3.o

四、参考资料

  1. Linux程序设计(第4版),Neil Matthew等著,人民邮电出版社,2010年
  2. http://mrbook.org/blog/tutorials/make/
  3. http://www.cs.colby.edu/maxwell/courses/tutorials/maketutor/
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/运维做开发/article/detail/965563
推荐阅读
相关标签
  

闽ICP备14008679号