什么是Makefile,以及uboot 的 makefile 语法分析(一)

1、什么是 Makefile

很久很久以前,人们不用 Makefile,生产一个程序,就只能手动敲命令。

一开始,程序很小很小,只有一个文件,只需要在shell里敲上:

   gcc main.c -o main

后来,程序越来越大,有非常多的文件,需要敲入这个:

    gcc main.c 1.c 2.c 3.c ... ... ... -o main

每次修改某个文件,都需要重新编译所有文件,要好久好久呀,编译效率非常低下。

有没有办法让提高效率呢?

    gcc -c main.c 1.c 2.c 3.c ... ... ... 分别生成 所有的.o文件;

    gcc main.o 1.o 2.o 3.o ... ... ... -o main 再把生成的.o文件链接成目标文件main;

如果修改 1.c,只需要 先

    gcc -c 1.c  生成 1.o 文件;

    gcc main.o 1.o 2.o 3.o ... ... ... -o main 

再把刚生成的 1.o,和其他之前生成的.o文件,一起链接成目标文件main

改哪个编哪个,编译效率倒是提高了,不过,敲命令,也够受了,如果改很多文件的话,还要记得,改了哪个,如果编译比较大型的工程,光是敲命令,都得敲到手抽筋,于是,make应运而生。

make机制:make的机制类似于“改哪个编哪个”,make 会自动检查源文件.c和对应的文件.o的最后修改时间,如果某个源文件.c的最后修改时间,比对应的文件.o要新,说明,这个文件修改过,需要重新编译,make就会自动编译对应的文件,再把新编译生成的新的对应文件.o,和之前已生成的.o文件,一起链接成目标文件xxxx。

那么,make 怎么知道我要编译哪些文件呢?是只编译1.c 2.c?还是编译全部?或者其它的呢?

这个,就看 Makefile 了,make是个大厨,而 Makefile 就是菜谱,菜谱里写着,菜名,原材料,加工方法等。

2、Makefile 是什么样

假如我们有三个源文件。

hellomake.chellofunc.chellomake.h
“#include <hellomake.h> int main() { // call a function in another file myPrintHelloMake(); return(0); }”“#include <stdio.h> #include <hellomake.h> void myPrintHelloMake(void) { printf(”“Hello makefiles!\n”"); return; }"“/* example include file */ void myPrintHelloMake(void);”

我们用命令行编译的话是这样

gcc -o hellomake hellomake.c hellofunc.c

如果写一个简单的makefile的话,是这样,文件名称保存为Makefile。

hellomake: hellomake.c hellofunc.c
     gcc -o hellomake hellomake.c hellofunc.c

基本格式要求:

target ... : prerequisites ...
    command
    ....
    ....

target : 目标文件, 可以是Object File 也可以是可执行文件,还可也是标签Label;

prerequisites: 生成target所需的文件或目标;

command: make需要执行的命令,可以是任何shell命令。

在linux的shell环境下,直接输入make,则会在当前的目录下查找名为“Makefile”或者“makefile”的文件。如果找到,它会把文件中第一个target作为最终的目标文件。

我们知道,一个程序文件编写完成后, 接下来的编译的流程是(.c .S) – > (.o .a) --> (.bin),但是make工具分析Makefile的过程是相反的。为了生成(.bin) 需要依赖(*.o .a),为了生成(.o .a)间接的又需要(.c .S),可以说(.c *.S)是最初的依赖。

3、Makefile 变量的声明和赋值。
PS:Makefile里所有变量都是字符串

变量的声明,方法有:
=、?=、:=、+=、
define  endef

变量的使用方法:$(var)

=、?=、define 延时变量,在使用的时候才确定值

:= 立即变量,在定义的时候就确定值

+= 看前面的,前面的是延时就是延时,前面的是立即就是立即

    var1=abc

    var2=$(var1)def

    var1=ghi

    echo $(var2) 

输出 ghidef

=、?=、define是延时变量,所以,在 echo $(var2) 时,才确定 var2的值是 $(var1)def,var1的值是 ghi



    var1=abc

    var2:=$(var1)def

    var1=ghi

    echo $(var2) 

输出 abcdef

:= 是立即变量,所以,在var2:=$(var1)def 时,就已经确定了var2的值是 abcdef



?=只有在变量第一次赋值的时候有效,也就是说,如果前面变量没被赋值,就赋值,如果有赋值,则不执行赋值,跳过。



    var1?=abc 或者 var1=abc 或者 var1:=abc

    var1?=def

    echo $(var1) 

输出 abc



    var1?=def

    echo $(var1) 

输出 def



+=字符串连接,在原有的字符串上加上后续内容,不过中间会有空格喔,呵呵哒



    var1=abc

    var1+=def

    echo $(var1) 

输出 abc def
4、Makefile 的结构

首先来个例子,假如我们有一个可执行文件,名称为edit,它依赖于8个目标文件,这些目标文件又依赖于8个c文件和3个头文件。

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
                   
//每一个目标文件的依赖关系
main.o : main.c defs.h
        cc -c main.c
kbd.o : kbd.c defs.h command.h
        cc -c kbd.c
command.o : command.c defs.h command.h
        cc -c command.c
display.o : display.c defs.h buffer.h
        cc -c display.c
insert.o : insert.c defs.h buffer.h
        cc -c insert.c
search.o : search.c defs.h buffer.h
        cc -c search.c
files.o : files.c defs.h buffer.h command.h
        cc -c files.c
utils.o : utils.c defs.h
        cc -c utils.c
clean :
        rm edit main.o kbd.o command.o display.o \
           insert.o search.o files.o utils.o

我们在shell命令行输入 make即可执行这个makefile,
如果输入make clean 执行的同时会把可执行文件edit和所有的目标文件删除。

一个文件如果这么写,显然太复杂、太冗长了,我们可以用变量定义的方式进行简化。

objects = main.o kbd.o command.o display.o \
          insert.o search.o files.o utils.o

edit : $(objects)
        cc -o edit $(objects)

$(objects) : defs.h
kbd.o command.o files.o : command.h
display.o insert.o search.o files.o : buffer.h

//这里类似伪代码,指明clean并不是一个文件,而是一个执行语句的缩写
.PHONY : clean
clean :
        rm edit $(objects)
5、Makefile 的通配符

为了书写上的便捷,makefile支持很多通配符语法,比如

CC=gcc
CFLAGS=-I.
DEPS = hellomake.h

%.o: %.c $(DEPS)
	$(CC) -c -o $@ $< $(CFLAGS)

hellomake: hellomake.o hellofunc.o 
	$(CC) -o hellomake hellomake.o hellofunc.o 

%.o: %.c $(DEPS) 这句定义了一个规则,意思是所有依赖于hellomake.h的.c文件都编译生成.o文件。
%在这里称为通配符。

我们还可以看到这种通配符

out.o: src.c src.h
$@ 表示 “out.o” (target)
$< 表示 “src.c” (first prerequisite)
$^ 表示 “src.c src.h” (all prerequisites)
例如:

CC=gcc
CFLAGS=-I.
DEPS = hellomake.h

%.o: %.c $(DEPS)
	$(CC) -c -o $@ $< $(CFLAGS)

意思是把第一个.c 文件编译后的文件作为target文件。
-I 表示在当前路径下操作。

6、Makefile 常用的函数。
字符串替换和分析函数:

$(subst from,to,text)

在text中使用to替换from.

echo $(subst ee,EE,feeet on the street!) 输出 fEEet on the strEEt!

$(patsubst pattern,repace,text)

在text中寻找符合pattern格式的字,用repace代替它。

$(patsubst %.c,%.o,aa.c.c bb.c) 输出 aa.c.o bb.o

$(strip string) 去掉前导和结尾,并把中间多个空格压缩成一个

$(strip I am a rich man!! ) 输出 I am a rich man!!

$(findstring find,string) 在string中寻找find,有则返回find,无则返回空

$(findstring rich man,I am a rich man!!) 输出 rich man

$(filter pat…,text) 返回在text中“匹配用空格隔开的pat…”的字,去除不匹配的字。

$(filter %h %n,I am a rich-man) 输出 rich-man

$(filter-out pat…,text) 返回在text中“匹配用空格隔开的pat…”的字,去除不匹配的字。

$(filter-out %h %n,I am a rich-man) 输出 I am a

$(sort list…) 去除list…中用空格隔开的重复的单词,并按字母排序,先符号,后数字,再字母,大小写区分

$(sort 1. I am a rich man !man Man a rich) 输出 !man 1. I Man a am man rich

文件名函数:

$(dir dirs…) 抽取每一部分的目录部分,目录从文件名的首字符起到最后一个/结束的字符

$(dir /mnt/src.c hack/ddr/aab.a kko.c) 输出/mnt/ hack/ddr/ ./

/mnt/src.c 的目录是 /mnt/

hack/ddr/aab.a 的目录是 hack/ddr/

kko.c 的目录是 ./

$(notdir names…) 抽取文件名

$(notdir /mnt/src.c hack/ddr/aab.a kko.c) 输出src.c aab.a kko.c

$(suffix names…) 抽取文件名后缀

$(suffix /mnt/src.c hack/ddr/aab.a kko.d rich-man) 输出.c .a .d

$(basename names…) 抽取除后缀外的其他字符

$(basename /mnt/src.c hack/ddr/aab.a kko.d rich-man) 输出/mnt/src hack/ddr/aab kko rich-man

$(addsuffix suffix,name…) 加文件名后缀

$(addsuffix .c,1413 1314 rich-man) 输出1413.c 1314.c rich-man.c

$(addprefix prefix,names…) 加前缀,比如 src/, hack.c bb.c 输出 src/hack.c

$(addprefix rich-,man womem boy girl) 输出 rich-man rich-womem rich-boy rich-girl

$(wildcard *.c) 返回当前文件夹下.c的文件,输出 1.c 2.c main.c

其他函数:

$(foreach var,list,text) 将list和var扩展,再将每个list赋给var,text引用var再进行扩展。

dirs:=a b c d e

files:=$(foreach tmp,$(dirs),$(tmp).txt)

echo $(files)

输出:a.txt b.txt c.txt d.txt e.txt

dir第1次扩展=a, tmp=a

dir第2次扩展=b, tmp=b

…………

最后tmp.txt就是 a.txt b.txt c.txt d.txt e.txt

$(if condition,then,else)

先把condition展开,如果非空,就执行then,如果空,就执行else。

file:=a b c d

$(if $(file),havefile,nothavefile) 返回havefile

#file:=

$(if $(file),havefile,nothavefile) 返回nothavefile

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值