Makefile基本语法

更新时间:2023-05-15 14:19:13 阅读: 评论:0

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

本文发布于:2023-05-15 14:19:13,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/fan/90/109427.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:命令   编译   需要   规则
相关文章
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图