编写简单的Makefile

1. 什么是Makefile?

相信大多数学过单片机的同学都用过各种IDE软件,例如visual c++、keil、IAR、eclipse、Visual Studio等等,这些软件为我们提供了很方便的工具,可以一键编译,最终得到我们想要的二进制文件。但是,这些软件屏蔽了太多编译过程,所以相信很多同学都不明白一个程序是如何编译的。而Makefile就完成了这个编译过程,程序的编译过程主要分为如下几步:

1.1 预处理

处理程序中所有带‘’#‘’的语句,主要的工作:头文件的包含和宏的定义。把#include包含进来的头文件插入到#include所在的位置,将#define定义的宏用对应的字符串替换。

1.2 编译

检查代码是否有语法错误。如果有错,则会报错,若没有错,则会把 .C 文件编译成 .S 文件。

1.3 汇编

将所有的 .S 文件转化为机器能识别的 .o二进制文 件。

1.4 链接

将所有的 .o 文件和需要的库文件链接成一个可执行文件。那么什么是需要的库文件?在程序中,我们经常会使用到printf打印,但是我们并未在程序中定义这个函数,而仅仅只是包含了#include <stdio.h>,在这个文件中只是对printf进行声明,而未定义。那么,printf到底在哪里被定义的呢?答案是在库文件中。系统把这些函数实现都定义到名为libc.so.6的库文件中去了,在没有特别指定时,编译器会到系统默认的搜索路径”/usr/lib”下进行查找。

2. 怎么来写Makefile

Makefile的核心是:规则。当执行make时,编译器会读取当前目录下的Makefile,然后根据Makefile的规则编译。
规则:
目标:依赖1 依赖2
命令

说明:使用命令,将依赖生成目标
注意:目标顶格写,命令前有一个TAB键,不能使用空格代替
命令执行条件:
a. “依赖”文件 比 “目标”文件 新
b. 没有“目标”这个文件
满足上述条件之一,命名就会执行

3. 以一个例子来讲解如何编写Makefile

现在就以我做Socket编程的一个客户端程序作为例子来讲解如何编写Makefile
例程:链接:http://pan.baidu.com/s/1pKRjXwR 密码:uaro

3.1 不使用Makefile

gcc -o client client.c text.c
使用命令行编译,即表示编译client.c text.c,生成目标文件client,
上面的命令行执行了哪些操作?
(1)对于client.c: 预处理、编译、汇编
(2)对于test.c: 预处理、编译、汇编
(3)将client.o test.o 库文件链接成可执行程序
即将上述四个步骤:预处理、编译、汇编、链接一次性完成。如果想查看具体的编译过程可使用-v选项,即gcc -v -o client client.c text.c
优点:命令简单,容易理解
缺点:如果文件很多,那么当只修改一个文件时,所有的文件都会被编译,所以很耗时

3.2 第一个Makefile

在源文件下新建一个Makefile文件,内容为:
test:a.c b.c a.h
gcc -o test a.c b.c
注意:第二行前有一个TAB键,不是空格
第一行:test表示执行make后生成的目标文件,a.c b.c a.h表示目标文件test依赖a.c b.c a.h这三个文件产生
第二行:使用这条命令来生成目标文件test
那么命令gcc -o test a.c b.c什么时候被执行呢?在前面我们已经讲过
命令执行条件:
a. “依赖”文件 比 “目标”文件 新
b. 没有“目标”这个文件
当我们第一次编译时,并没有目标文件test,满足条件b,所以命令得到执行
当我们第一次执行make时,会编译生成test,当再次执行make时,编译器会提示目标文件已经是最新的,即没有使用gcc -o test a.c b.c编译,为什么呢?因为有“目标”文件,且没有“依赖“比”目标文件”新,即上面的两个条件都不满足,所以命令不会执行。那么如何让命令再次执行?
方法1:删除目标文件test
方法2:修改a.c b.c a.h会使依赖文件比目标文件新

缺点:如果文件很多,那么当只修改一个文件时,所有的文件都会被编译,所以很耗时

3.3 改进

Makefile:
test:a.o b.o
gcc -o test a.o b.o
a.o:a.c
gcc -c -o a.o a.c
b.o:b.c
gcc -c -o b.o b.c

3.3.1 分析

test:a.o b.o:test目标依赖a.o b.o二进制文件
a.o a.c:a.o依赖a.c
b.o b.c:   b.o依赖b.c
执行过程:
gcc -c -o a.o a.c
gcc -c -o b.o b.c
gcc -o test a.o b.o

3.3.2 优点

3.3 比 3.2的优点:
3.2 若修改a.c,则会编译所有的C文件
3.3 若修改a.c,则只会编译a.c,不会编译b.c,达到节省时间的目的
执行过程:
gcc -c -o a.o a.c
gcc -o test a.o b.o

3.4 再次改进

test:a.o b.o
gcc -o test a.o b.o
%.o:%.c
gcc -c -o $@ $<

3.4.1 分析

如果a.o b.o c.o ...有一万多个文件,难道要写一万次?
解决方法:使用通配符
$@:输出
$<:第一个依赖
$^:所有的依赖


3.4.2 缺点1

若修改a.h,则不会重新编译,改进:
test:a.o b.o
gcc -o test a.o b.o
a.o:a.c a.h
%.o:%.c
gcc -c -o $@ $<


3.4.3 缺点2

不可能每个.c文件都自己确定依赖哪些.h文件,解决方法参考下例


3.5 最后一次改进

3.5.1 如何确定文件依赖的头文件

如何使用gcc生成依赖文件,方法:随便修改linux内核的某个文件,查看编译结果,看到arm-linux-gcc -Wp,-MD,fs/.file.o.d -nostdinc -isystem ... /*fs/.file.o.d即为依赖文件*/
gcc -c -o a.o a.c -Wp,-MD,a.d /*依赖文件名自己选定,设为a.d*/
vi a.d /*查看依赖文件*/

3.5.2  编写Makefile

objs := a.o b.o
test:$(objs)
gcc -o test $^
# .a.o.d .b.o.d
dep_files := $(foreach f,$(objs),.$(f).d)
dep_files := $(wildcard $(dep_files))
ifneq ($(dep_files),)
  include $(dep_files)
endif
%.o : %.c 
gcc -Wp,-MD,.$@.d -c -o $@ $<
clean:
rm *.o test


上述的源码:链接:https://github.com/1685550794/Makefile

注明:本文只是介绍简单的Makefile的编写,对于复杂的Makefile,有机会再讲

若引用本文,请注明出处
















  • 12
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值