MakeFile基础入门用法简介

很久以前写的一篇技术备忘了,隐约记得当时是看了APUE 2nd Edition这本书后,下载的源码在CentOS7以及MacOX10.X上都编译不过。在解决编译问题的同时,被APUE源码中的MakeFile中的天书给震惊后,学习了一些相关入门知识。并以看懂并向后来的好奇宝宝们解释APUE的MakeFile含义作为小目标,记录了这篇文章。

1. MakeFile的基础格式

target...:prerequisites
    command
    ...

target 就是我们需要生成的目标文件。可以是可执行文件,也可以是object file中间文件,还可以是其他的标签
prerequisites 就是target所依赖的文件
command 就是生成target需要执行的命令,特别注意的是所有的shell command都可以在这里执行

下面就是一个最朴实的MakeFile文件,没有使用任何技巧

all:foo
foo:hello.o world.o
    gcc -o foo hello.o world.o -I.
hello.o:hello.c hello.h world.h
    gcc -c hello.c
world.o:world.c world.h
    gcc -c world.c
clean:
    rm hello.o world.o

2. 隐晦(高级)规则

我们可以给需要编译的这些文件名称用一个变量来代替,有点像常量的使用方法一样
其实我们看到的那些开源代码的Makefile用了很多隐晦规则
比如test.o一般来说对应的源文件就是test.c,并且调用编译器生成[.o]文件也是固定的。那么我们就不用像上面那样这样写了,而是像下面一样省略

objects=hello.o world.o
all:foo
foo:$(objects)
    gcc -o foo $(objects)
hello.o:hello.h world.h
world.o:world.h
.PHONY:clean
clean:
    rm $(objects)

注释和shell脚本一样用#号,记得命令必须以tab开头
命令前面加一个-减号是告诉make忽略这条命令可能引起的错误
代表了$HOME目录,fengzhao代表了某位

自动化变量
$? 所有比target更新的prerequisites,以空格间隔,如下所示

print:*.c
    lpr -p $?

特别注意的是,make为变量赋值一个含有通配符的变量需要用上wildcard函数,否则通配符就不会被解释
objects=.o #objects真的就只是.o而已
objects:=$(wildcard *.o)#objects会用通配符去展开这个变量得到当前目录下面的中间文件
:= 和 = 的区别是

A=$(B)
B=hello.c world.c#上一行还没有定义B,但是用=已经可以引用到后面定义的B,然后给A赋值

B:=hello.c world.c
A:=$(B)#用:=进行必须引用已经定义了的变量

==?=== 的意思是 hello?=world 如果hello没有定义的话,它就等于world。如果定义了的,那么什么都不做

3. 多个target的语法

target可以是多个
静态模式的形式如下

target:target-pattern:prereq-pattern
    command
    ...

举个例子来说明

objects=hello.o world.o
$(objects):%.o:%.c
    $(CC) -c $(CFLAGS) $< -o $@

第二行的意思就是目标是objects里面那些[.o]文件,把xxx.o中的xxx取出来,找到的xxx.c就是其依赖的文件

==$<== 表示的 prereq-pattern 指代的文件,在这里就是 hello.c world.c
==$@== 表示的 target 指代的文件,在这里就是 hello.o world.o
再看一个加入了filter函数的例子

objects=hello.o foo.elc world.o
$(filter %.o, $(objects)):%.o:%.c
    $(CC) -c $(CFLAGS) $< -o $@
$(filter %.elc, $(objects)):%.elc:%el
    emacs -f batch-byte-compile $<

这里的filter就是一个make build-in函数。函数名和参数用空格分隔,参数之间用,分隔

source=hello.c world.c
include ($source:.c=.d)

这个语法的意思是用.d来替换source中的.c(.c必须是结尾处的),然后形成新的新的集合

source=hello.c world.c
include ($source:%.c=%.d)

该语法的效果同上,只不过用的是静态规则

在命了前面加上 @ 可以让命令不显示出来
如果要执行多条(行)命令,且后面的命令依赖前面的命令,需要在命令之间加上 ; 号,并且写在一行
==$^== 表示 prereq 指代的文件集合并且去重
==$*== 表示模式 % 符号前面的部分

x=var1
var2=Hello
y=$(subst,1,2,$(x))

这里的函数subst是把$(x)中的1全部替换成2,所以y最后等于Hello

4. 其它更老的规则

模式变量

prog:CFLAGS=-g

目标变量

%.o:CFLAGS=-O

这两种变量都是,当且仅当特定的目标需要被执行打时候,用设置的变量取代环境变量的值

后缀规则,其实就是老式的隐含规则

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

库文件的目标是

helloworld.a(hello.o world.o):hello.o world.o
    ar cr helloworld.a hello.o world.o

看APUE的make发现它的makefile写的那叫一个简单,能用隐含规则的全部用上了
比如 all: helloworld 就这么一句,合理推测的话,make会去自动寻找helloworld.c并去调用对应compile规则
比如 objs: hello.o world.o 也是一句,道理同上
还需要注意的是

编译 .c 文件到可执行文件的隐含规则的名字叫

COMPILE.c=$(CC) $(CFLAGS) $(CPPFLAGS) -c

链接 .c 文件到obj文件的隐含规则的名字叫

LINK.c=$(CC) $(CFLAGS) $(CPPFLAGS) $(LDDIR) $(LDFLAGS)

没想到看了上面这么多的内容,APUE的makefile还是遇到了小挑战,不过思考后还是解决了

GCC

gcc -c hello.c //将只生成 hello.o

链接

  • ar 命令的作用是把obj文件,打包成一个 .a 库文件,ar rv libtest.a hello.o fibo.o 便于其他的程序链接 libtest.a。其他程序通过 -ltest 就可以链接到这个库
  • nm 命令的作用是查看 ar 命令打包好的 .a 库文件里面由哪些符号构成

你可能感兴趣的:(MakeFile基础入门用法简介)