Makefile 基本语法
1、Makefile 基本格式:
issues是什么意思make所看到的第⼀项规则会被当做默认规则使⽤。
⼀个规则可分成三个部分:
⽬标:依赖1 依赖2。。。
命令
注意:命令前必须使⽤ 有⼀个制表符(<TAB>) 。
例:
foo.o: foo.c foo.h
gcc -c foo.c
当要求make执⾏⼀条规则时,它⾸先找到⽬标和前提条件指出的⽂件。如果没有任何前提条件都有⼀条关联规则,那么make将试图先更新性前提条件。接着,更性⽬标⽂件。
如果任何前提条件都⽐⽬标要新,⽬标将通过执⾏命令得以更新。
每条命令都传给shell并在它的⼦shell中执⾏。如果任何⼀条命令产⽣错误,⽬标的创建将被终⽌,随后make退出。如果⼀个⽂件最近被修改,那就认为它⽐其他的⽂件新。 GNU make 规则:
target — ⽬标⽂件, 可以是Object File 也可以是可执⾏⽂件,还可也是标签Label (标签内容在“伪⽬标”章节);
prerequisites —⽣成target 所需的⽂件或⽬标;
command—make 需要执⾏的命令,可以是任何shell 命令。
2、⼀个简单的例⼦
创建⼀个名为count_word.c 的⽂件,代码如下
另外创建⼀个lexer.l ⽂件,其中所有的空⽩均为tab 键
最后创建makefile ⽂件,内容为:
target ... : prerequisites ...
command
....
....[cpp]
01. #include <stdio.h> 02. extern int fee_count, fie_count, foe_count, fum_count; 03. extern int yylex( void ); 04. 05. int main( int argc, char ** argv ){ 06. yylex( ); 07. printf( "%d %d %d %d\n", fee_count, fie_count, foe_count, fum_count ); 08. return ( 0 ); 09. }
[cpp]
01. int fee_count = 0; 02. int fie_count = 0; 03. int foe_count = 0; 04. int fum_count = 0; 05. %% 06. fee fee_count++; 07. fie fie_count++; 08. foe foe_count++; 09. fum fum_count++;
以上内容保存在Makefile 或者是makefile 都可以,直接输⼊make 命令就可以⽣成可执⾏⽂件count_words 了;
如果要删除执⾏⽂件和中间的⽬标⽂件,那么就执⾏⼀下make clean 。
注意1: 当依赖关系定好后,下⾯⼀⾏就是如何⽣成⽬标⽂件的操作系统命令了,⼀定要以⼀个Tab 键开头。 另外,make 会⽐较targets ⽂件和prerequisites ⽂件的修改⽇期,如果prerequisites ⽂件的⽇期⽐targets ⽂件新,或者targets 不存在,那么make 就会执⾏这下⾯⼀⾏的系统命令。
注意2: clean 不是⼀个⽂件,它是⼀个动作名,冒号后⾯什么都没有,make 就不会⾃动去找它的依赖性,也不会执⾏它后⾯的系统命令。因此,要执⾏clean 就需要显式的指出make clean 。
美国名校公开课注意3: 如果报错,可能需要先安装flex :
注意4: 运⾏count_works 后,它会回显你的输出并统计'fee','fie','foe','fum'的次数。结束统计需要按Ctrl+d ,然后会输出四个单词出现的次数。
三、make 如何⼯作
默认⽅式
直接输⼊make ,则
1. make 会在当前的⽬录下找到名为“Makefile”或者“makefile”的⽂件。
2. 如果找到,它会把⽂件中第⼀个target 作为最终的⽬标⽂件(如上⾯例⼦中的count_words )。
1. ⾸先,make 会检查⽬标count_words 的prerequisite ⽂件count_words.o, lexer.o 和 -lfl 。
2. count_words.o 通过编译count_words.c ⽣成
3. lexer.o 通过编译lexer.c ⽣成,但是lexer.c 并不存在,因此会继续寻找lexer.c 的⽣成⽅式,并找到了通过flex 程序将lexer.l ⽣成为
lexer.c 。
4. 最后,make 会检查-lfl ,-l 是gcc 的⼀个命令选项,表⽰将系统库链接到程序。⽽"fl"对应的是libfl.a 的库。(GNU make 可以识别
这样的命令,当⼀个prerequisite 是以这种-l<name>的形式表⽰出来的时候,make 会⾃⼰搜索lib<name>.so 的库⽂件,如果没找
到则继续搜索lib<name>.a 的库⽂件)。这⾥make 找到的是/usr/lib/libfl.a ⽂件,并将它与程序进⾏连接。
3. 如果count_words ⽂件不存在,或者count_words 所依赖的后⾯的.o ⽂件的修改时间⽐count_words 本⾝更加新,那么,它会执⾏后⾯定义的命令来⽣成
这个count_words ⽂件。如果count_words 所依赖的.o ⽂件也不存在,那么make 会继续按照前⾯的⽅式⽣成.o ⽂件。
4. 找到相应的.c 和.h ,⽤来⽣成.o ,然后再⽤.o 完成make 的最终任务。
关于依赖关系
make 会⼀层⼀层的去找⽂件的依赖关系,最终编译出第⼀个⽬标⽂件。
关于重新编译
只要任何prerequisite ⽐ target 新,那么这个⽬标⽂件就会被下⾯的命令重新⽣成。每⼀个命令都会被传递到shell 中,并在⾃⼰的⼦shell ⾥⾯执⾏。
关于错误
如果在寻找过程中出现错误,如⽂件找不到,则make 会直接退出并报错。对于所定义的命令错误或者编译不成功,make 是不会理会的,它只负责⽂件的依赖性。
[cpp]
01. count_words: count_words.o lexer.o -lfl 02. gcc count_words.o lexer.o -lfl -o count_words 03. count_words.o: count_words.c 04. gcc -c count_words.c 05. lexer.o: lexer.c 06. gcc -c lexer.c 07. lexer.c: lexer.l 08. flex -t lexer.l > lexer.c 09. clean: 10. rm lexer.c lexer.o count_words.o count_words
gcc -c count_words.c
flex -t lexer.l > lexer.c
gcc -c lexer.c
gcc count_words.o lexer.o -lfl -o count_words
[plain]
chinechess01. sudo apt-get install flex
上⾯的例⼦可以看到,⽂件或者⽬标的名字⼏乎都毫⽆例外的出现了⾄少两次,甚⾄如果算上clean 的内容,有些⽂件名出现了三次。然⽽,在⼀个⼤型的⼯程中这种情况会更加复杂,任何不经意的错误都会导致编译失败。为了让makefile 更容易维护,在makefile 中我们可以使⽤变量,或者更确切的说是⼀个字符串,类似c 语⾔中的宏。例如:信星
五、⾃动推导依赖关系
GNU make 可以根据.o ⽂件的⽂件名⾃动推导出同名的.c ⽂件并加⼊依赖关系,不需要我们⼿动注明。并且gcc -c 也会被⾃动推导出来,于是我们的makefile 就变成了
这种⽅法也叫“隐式规则”。
六、关于Clean
⼀个好习惯是每个makefile 都要写clean 规则,这样不仅可以⽅便重编译,也有利于保持⽂件路径的清洁。⼀般的风格是:
但是,更为稳妥的做法是:
.PHONY 表⽰clean 是⼀个“伪⽬标”,⽽rm 命令前⾯的减号则表⽰,不管出现什么问题都要继续做后⾯的事情。
注意:clean 规则不要放在makefile 的开头,不然就会变成make 的默认⽬标了。
拉瓦尔⼀、显式规则(Explicit Rules )
通常在写makefile 时使⽤的都是显式规则,这需要指明target 和prerequisite ⽂件。⼀条规则可以包含多个target ,这意味着其中每个target 的prerequisite 都是相同的。当其中的⼀个target 被修改后,整个规则中的其他target ⽂件都会被重新编译或执⾏。
通配符(Wildcards )
make ⽀持的通配符与Bourne shell 基本相同,包括~, *, ?, [...], [^...]。
*.* 表⽰了所有⽂件;
表⽰任意单个字符;
[...] 表⽰⼀个字符类;
laredo texas[^...] 表⽰相反的字符类。
~ 表⽰当前⽤户的/home 路径,~加⽤户名可以表⽰该⽤户的/home 路径。
注意 : make 会在读取makefile 的时候就⾃动补充好通配符替代的内容,⽽shell 则是在执⾏命令的时候才会进⾏通配符替代,在某些复杂情况,这两种⽅式会有[cpp]rococo
01. CC = gcc 02. object = lexer.o count_words.o 03. count_words: $(object) -lfl 04. $(CC) $(object) -lfl -o count_words 05. count_words.o: count_words.c 06. $(CC) -c count_words.c 07. lexer.o: lexer.c 08. $(CC) -c lexer.c 09. lexer.c: lexer.l 10. flex -t lexer.l > lexer.c 11. clean: 12. rm lexer.c $(object) count_words
[cpp]
01. CC = gcc 02. object = lexer.o count_words.o 03. count_words: $(object) -lfl 04. $(CC) $(object) -lfl -o count_words 05. count_words.o: 06. lexer.o: 07. lexer.c: lexer.l 08. flex -t lexer.l > lexer.c 09. clean: 10. rm lexer.c $(object) count_words
[cpp]
01. clean: 02. rm lexer.c $(object) count_words
[cpp]
01. .PHONY: clean 02. clean: 03. -rm lexer.c $(object) count_words
伪⽬标(Phony Targets )
多数情况下makefile 中的target ⽬标⽂件是像前⾯提到的那样带有指定的prerequisite ⽂件,但也有⼀些target 仅仅是作为⼀个标签,代表了⼀条命令,这种不代表任何⽂件的⽬标就被称为伪⽬标。常见的伪⽬标例如在makefile 开头部分的第⼀个⽬标 all , 以及前⾯例⼦中见到的 clean :
但是,make 本⾝是⽆法区别出⽬标⽂件和伪⽬标的,如果碰巧在编译路径下有⼀个与伪⽬标同名的⽂件存在,那么make 会在依赖关系图中把这个⽂件与伪⽬标名相关联。⽽再运⾏make clean 命令则会因为clean ⽂件存在且如果没有被更新过,则makefile 中的clean 对应的命令将不会被执⾏。
为了避免这种情况,GNU make 提供了⼀种特殊⽬标: “.PHONY”,⽤来表⽰⽬标⽂件不是真正的⽂件,即伪⽬标。clean 命令可以被写作:
这样,即使再有名为clean 的⽂件存在,make 也会执⾏clean 后⾯的命令。
通常不会将⼀个伪⽬标的prerequisite 设置为真是存在的⽂件,因为.PHONY 会让他后⾯的⽂件在每次make 时都进⾏重新编译。伪⽬标可以被认为是内嵌在makefile 中的shell 脚本。
优势 : 通过使⽤伪⽬标在编译过程中进⾏屏幕输出,可以使make 的可读性增加。例如:
这⾥将printf 命令作为伪⽬标,可以使make 在更新任何prerequisite 之前就将指定的编译信息输出。
同时,伪⽬标也可也作为makefile 的默认⽬标,放在⽂件的最前端,由于伪⽬标的特性,他指出的所有prerequisite 都会被重新编译。这样可以⽤来同时⽣成多个⽬标。另外,与普通⽬标⽂件⼀样,伪⽬标也可也使⽤依赖关系,例如:
这样就可以对不同类型的⽂件进⾏单独删除。
空⽬标(Empty Targets )
空⽬标是伪⽬标的⼀种变形形式,通常情况下通过创建⼀个空⽂件来实现。例如:
这样,空⽂件size 就被make 当作时间戳,只有当count_words.o 被更新时,size ⾥⾯的命令才会再次被执⾏。另外,所有加载了size 作为prerequisite 的⽬标,都不会因为size 被编译⽽强制编译,他们的其他prerequisite ⽬标被更新。
⼆、变量
变量最简单的形式就是:
变量可以包含⼏乎所有的字符包括标点符号。⼀般情况下,变量名需要被$( )所包裹,但是当变量名只有⼀个字符时,括号可以省略。makefile 可以定义很多变[plain]
01. clean: 02. rm -f *.o lexer.c [plain]
单词造句01. .PHONY: clean 02. clean: 03. rm -f *.o lexer.c [plain]
01. $(Program): build_msg $(OBJECTS) $(BUILTINS_DEP) $(LIBDEP) 02. $(RM) $@ 03. $(CC) $(LDFLAGS) -o $(Program) $(OBJECTS) $(LIBS) 04. ls -l $(Program) 05. size $(Program) 06. .PHONY: build_msg 07. build_msg: 08. @printf "#\n# Building $(Program)\n#\n"
会计工作总结怎么写[plain]
01. .PHONY: clean cleano cleanc 02. clean: cleano cleanc 03. -rm $(program) 04. cleano: 05. -rm *.o 06. cleanc: 07. -rm lexer.c
[plain]
01. size: count_words.o 02. size $^ 03. touch size
$(variable_name)
量,但同时make 本⾝也定义了⼀些⾃动变量。
⾃动变量
⾃动变量是make ⾃动根据规则⽣成的,不需要⽤户显式的指出相应的⽂件或⽬标名称。以下就是七个最核⼼的⾃动变量:
$@ ⽬标⽂件的⽂件名;
$% 仅当⽬标⽂件为归档成员⽂件(.lib 或者 .a )时,显⽰⽂件名,否则为空;
$< 依赖(prerequisite )列表⾥⾯的第⼀个⽂件名;
$? 所有在prerequisite 列表⾥⾯⽐当前⽬标新的⽂件名,⽤空格隔开;
$^ 所有在prerequisite 列表中的⽂件,⽤空格隔开; 如果有重复的⽂件名(包含扩展名),会⾃动去除重复;
$+ 与$^相似,也是prerequisite 列表中的⽂件名⽤空格隔开,不同的是这⾥包含了所有重复的⽂件名;
$* 显⽰⽬标⽂件的主⼲⽂件名,不包含后缀部分。
此外,上⾯的每个变量都带有两个不同的变种,⽤于适应不同种类的make 。分别是在后⾯附加⼀个“D”或者“F”。例如,$(^D)就是代表所有依赖⽂件的路径,$(<F)表⽰依赖⽂件第⼀个的⽂件部分的值。使⽤上述内容前⾯的makefile 可以重写为:
三、 查找⽂件(VPATH )
上⼀篇所使⽤的例⼦中,makefile 和源⽂件都是在同⼀个简单⽬录下,但真正的程序往往会复杂很多。让我们重新修改整个程序,添加⼀个叫做counter 的函数,同时添加counter.c:
为了使这个库函数具有复⽤性,再添加⼀个counter.h 作为头⽂件声明:
[plain]
01. CC = gcc 02. object = lexer.o count_words.o 03. program = count_words 04. $(program): size $(object) -lfl 05. $(CC) $(object) -lfl -o $@ 06. count_words.o: count_words.c 07. $(CC) -c $^ 08. lexer.o: lexer.c 09. $(CC) -c $^ 10. lexer.c: lexer.l 11. flex -t $^ > $@ 12. size: count_words.o 13. size $^ 14. touch size 15. .PHONY: clean cleano cleanc 16. clean: cleano cleanc 17. -rm $(program) 18. cleano: 19. -rm *.o 20. cleanc: 21. -rm lexer.c
[cpp]
01. #include <lexer.h> 02. #include <counter.h> 03. 04. void counter( int counts[4]) { 05. while ( yylex() ) 06. ; 07. 08. counts[0] = fee_count; 09. counts[1] = fie_count; 10. counts[2] = foe_count; 11. counts[3] = fum_count; 12. }
[cpp]
01. #ifndef COUNTER_H_ 02. #define COUNTER_H_ 03. 04. extern void counter( int counts[4]); 05. #endiflaoda