Makefile教程

来源:互联网 发布:推荐淘宝男装店铺 编辑:程序博客网 时间:2024/06/06 19:39

编译:(编译器自动检测语法、函数与变量的声明是否正确)
源文件->中间目标文件([.o]/[.obj])
链接:(链接函数和全局变量,只管中间目标文件,不管源文件)
中间目标文件->执行文件
库文件(Library File)[.lib]文件:
有时编译生成的中间目标文件太多,给中间目标文件打个包->库文件
在UNIX下,是Archive File即[.a]文件。


Makefile规则:(prerequisites中如果有一个以上的文件比target文件要新的话,command所定义的命令就会被执行)
target :prerequisites
 [Tab]command

targets : prerequisites ; command

target:可以是执行文件、[.o]文件、标签。
prerequisites:要生成那个target所需要的文件或是目标,依赖文件。
[Tab]command:make需要执行的命令。(任意的Shell命令)如果其不与"target:prerequisites"在一行,那么,必须以[Tab键]开头,如果和prerequisites在一行,那么可以用分号做为分隔。


cc -c和-o命令:
-g 可执行程序包含调试信息
-o 指定输出文件名
-c 只编译不链接

cc -o [.o](后面带.o文件)
如:
edit : main.o kbd.o command.o display.o \
           insert.o search.o files.o utils.o
 cc -o edit main.o kbd.o command.o display.o \
                       insert.o search.o files.o utils.o

cc -c [.c](后面带.c文件)
如:
main.o : main.c defs.h
            cc -c main.c


make工作流程:(各层文件的依赖性:[.c]/[.h]->[.o]->最终target)
1、在当前目录下寻找文件名为Makefile或makefile的文件;
2、找到后,再找第一个目标文件(target),如以上的edit 文件,将其作为最终的目标文件;
3、如果edit 文件不存在,或者edit 后面所依赖的[.o]文件修改时间比edit这个文件新,才会执行后面定义的命令来重新生成edit这个文件;否则使用当前的edit 文件。
4、如果edit所依赖的[.o]文件也存在,则同样会判断[.o]文件的修改时间,决定是否执行[.o]后面的命令;如果不存在,这回先执行命令生成[.o]文件,再重新生成edit;
5、生成[.o]的[.c]和[.h]文件肯定是一直存在的(除非写代码时出错),这时make会自动报错并停止。


make中变量的使用:

定义变量:
objects = main.o kbd.o command.o display.o \
              insert.o search.o files.o utils.o
使用变量:
$(objects)
如:
edit : $(objects)
            cc -o edit $(objects)
main.o : main.c defs.h
            cc -c main.c
......
......
clean :
            rm edit $(objects)
作用:
当需要修改某个文件名称时(如main.o),或加入一个新文件,只需在定义变量objects = ...后面修改和添加,避免遗漏修改导致错误。


make自动推导("隐晦规则"):
make看到一个[.o]文件,会自动的把[.c]文件加在依赖关系中,且命令cc -c [.c]也会被自动推导。

main.o : main.c defs.h
            cc -c main.c
可以改成:
main.o : defs.h

 

[.o]和[.h]的依赖简化(尽量少用)
多个[.o]文件依赖同个[.h]文件,可将其合并,合并后不影响[.o]自动推导[.c]文件的规则。
如:
kbd.o : defs.h command.h
command.o : defs.h command.h
files.o : defs.h buffer.h command.h
可改为:
kbd.o command.o files.o : command.h
缺点是:文件多了则依赖性显得凌乱,不能一目了然。


清空目标文件,clean
clean文件是一个动作名字/标签,一般放在最后
语法:
clean:(后面不带任何中间目标文件)
 [Tab]命令
如:
clean :
 rm edit $(objects)  

 -rm edit $(objects)    #-rm前的"-"表示某些文件出现问题时,忽略并继续执行后面的操作
执行make时,(如果clean没有被其他目标文件直接或间接关联),make不会自动去找clean文件的依赖性,也就不会自动执行clean后面的命令,要执行后面的命令,需要在make命令显示地指出这个动作名字/标签。
作用:makefile可以定义不用编译的或和编译无关的命令,如打包、备份等。


.PHONY 伪目标
1、假如我们需要书写这样一个规则:规则中的命令不是创建目标文件,而是通过make命令明确指定它来执行一些特定的命令,例如clean目标。
clean:
 rm *.o temp
    这个命令当当前目录下没有名称为"clean"的文件时,使用make clean总是可以执行后面的rm命令。但当当前目录下存在名称为"clean"的文件,由于没有规则创建或修改clean这个目标文件(即这个目标不存在依赖性),因此这个目标文件总是最新的(没有最新的依赖文件)。根据make的规则,目标文件clean为最新,后面的rm命令当然也永远不会被执行。
    为解决以上问题,需要将目标"clean"声明为伪目标。将一个目标声明为伪目标的方法是将它作为特殊目标.PHONY"的依赖。
如:
.PHONY : clean
clean :
 -rm *.o temp
    这样目标"clean"就被声明为一个伪目标,无论在当前目录下是否存在"clean"这个文件。我们输入"make clean"之后。"rm"命令都会被执行。

2、在Makefile中,一个伪目标可以有自己的依赖(可以是一个或者多个文件、一个或者多个伪目标)。在一个目录下如果需要创建多个可执行程序,我们可以将所有程序的重建规则在一个Makefile中描述。因为Makefile中第一个目标是"终极目标",约定的做法是使用一个称为"all"的伪目标来作为终极目标,它的依赖文件就是那些需要创建的程序。
如:
假设all是Makefile的第一个目标(即最终目标)
all : prog1 prog2 prog3
.PHONY : all
  prog1 : prog1.o utils.o
 cc -o prog1 prog1.o utils.o
  prog2 : prog2.o
 cc -o prog2 prog2.o
  prog3 : prog3.o sort.o utils.o
 cc -o prog3 prog3.o sort.o utils.o
    执行make时,目标"all"被作为终极目标。为了完成对它的更新,make会创建(不存在)或者重建(已存在)目标"all"的所有依赖文件(prog1、prog2和prog3)。当需要单独更新某一个程序时,我们可以通过make的命令行选项来明确指定需要重建的程序。(例如:"make prog1")。

3、当一个伪目标作为另外一个伪目标依赖时,make将其作为另外一个伪目标的子例程来处理(可以这样理解:其作为另外一个伪目标的必须执行的部分,就行C语言中的函数调用一样)。
如:
.PHONY: cleanall cleanobj cleandiff
cleanall : cleanobj cleandiff
rm program
cleanobj :
rm *.o
cleandiff :
rm *.diff
    “cleanobj”和“cleandiff”这两个伪目标有点像“子程序”的意思(执行目标“clearall时会触发它们所定义的命令被执行”)。我们可以输入“make cleanall”和“make cleanobj”和“make cleandiff”命令来达到清除不同种类文件的目的。例子首先通过特殊目标“.PHONY”声明了多个伪目标,它们之间使用空各分割,之后才是各个伪目标的规则定义。
说明:
    通常在清除文件的伪目标所定义的命令中“rm”使用选项“–f”(--force)来防止在缺少删除文件时出错并退出,使“make clean”过程失败。也可以在“rm”之前加上“-”来防止“rm”错误退出,这种方式时make会提示错误信息但不会退出。为了不看到这些讨厌的信息,需要使用上述的第一种方式。
   

RM
    make存在一个内嵌隐含变量“RM”,它被定义为:“RM = rm –f”。因此在书写“clean”规则的命令行时可以使用变量“$(RM)”来代替“rm”,这样可以免出现一些不必要的麻烦!


make的注释
"#"后面表示注释,如果要使用"#"符号,则用反斜杠转义,如"\#"。


Makefile的文件名
make命令后会按顺序在当前目录寻找文件名为"GNUmakefile"、"makefile"、"Makefile"的文件。一般用"Makefile";
也可以用
make -f [文件名]或make --file [文件名]
指定make文件:如make -f my.makefile或make --file myfile.4M


引用其他的Makefile文件
include Filename
Filename可以含绝对路径和通配符
如:
include a.make b.make c.make d.mk e.mkk f.mkk
也可写成:
EF = e.mkk f.mkk
include d.mk *.make $(EF)

    make时,会把include包含的其他Makefile内容放在当前位置,如果文件名有包含路径,会首先在该路径下找该文件,如果没有该文件的话再回到当前目录下找,如果没有,还会在下面的几个目录下找:
    1、如果make执行时,有"-I"或"--include-dir"参数,那么make就会在这个参数所指定的目录下去寻找。
    2、如果目录<prefix>;/include(一般是:/usr/local/bin或/usr/include)存在的话,make也会去找。

    如果有文件没有找到的话,make会生成一条警告信息,但不会马上出现致命错误。它会继续载入其它的文件,一旦完成makefile的读取,make会再重试这些没有找到,或是不能读取的文件,如果还是不行,make才会出现一条致命信息。如果你想让make不理那些无法读取的文件,而继续执行,你可以在include前加一个减号"-"。如:
    -include Filename
    其表示,无论include过程中出现什么错误,都不要报错继续执行。和其它版本make兼容的相关命令是sinclude,其作用和这一个是一样的。


使用通配符
"*"、"?"和"[...]"3个
"*":注意如果定义变量objects = *.o ,这时[*.o]不会展开,即objects 就是 *.o,如果要让通配符在变量展开,可使用:objects := $(wildcard *.o)


总结make的工作方式
    1、读入所有的Makefile。
    2、读入被include的其它Makefile。
    3、初始化文件中的变量。
    4、推导隐晦规则,并分析所有规则。
    5、为所有的目标文件创建依赖关系链。
    6、根据依赖关系,决定哪些目标要重新生成。
    7、执行生成命令。

原创粉丝点击