文档章节

Makefile入门

realsa
 realsa
发布于 2016/10/01 17:24
字数 1752
阅读 811
收藏 53

1 手动编译链接

我们知道源文件生成可执行文件的过程可能需要一些依赖文件(头文件或者其他源文件)。[2]中提到对于C语言,产生可执行程序包括这样的步骤:

  • 1 预处理源文件(.c)
    • 替换预处理命令(如 #define)
    • 展开头文件(.h,包括静态链接库的头文件)到引用的源文件
  • 2 依次编译处理过的源文件,然后进行汇编,生成对应的目标文件(.o)
  • 3 链接(静态链接)目标文件和静态链接库(静态链接库的源文件生成的目标文件,.a),生成可执行的二进制文件。

我们假设这里有三个c文件:

hello.c

#include<stdio.h>//sys lib head  file
int main()
{
  int a=33;
  int b=22;
  printf("Min value is %d\n",min(a,b));
  printf("Max value is %d\n",max(a,b));
  return 0;
}

max.c

int max(int a,int b)
{
  return a>b?a:b;
}

min.c

int min(int a,int b)
{
  return a<b?a:b;
}

很明显,hello.c编译必须依赖max.c和min.c。我们手动编译生成可执行文件,并执行:

fsj@ubuntu:~/templates$ ls
hello.c  max.c  min.c
fsj@ubuntu:~/templates$ gcc -o hello hello.c max.c min.c 
fsj@ubuntu:~/templates$ ls
hello  hello.c  max.c  min.c
fsj@ubuntu:~/templates$ ./hello 
Min value is 22
Max value is 33

源文件不多的时候算麻烦了,那一个大工程中几十上百源文件,依赖关系错综复杂的时候怎么编译?这就要借助make工具了。

2 借助make自动编译链接

make是软件开发中常用的工具程序(Utility software),常被用来构建C程序。make通过读取叫做makefile或者Makefile的文件,来自动化建构软件。make命令编译和链接多个源文件的规则是[3]:

  • 1)如果这个工程没有编译过,那么我们的所有C文件都要编译并被链接。
  • 2)如果这个工程的某几个C文件被修改,那么我们只编译被修改的C文件,并链接目标程序。
  • 3)如果这个工程的头文件被改变了,那么我们需要编译引用了这几个头文件的C文件,并链接目标程序。

make工具通过源文件的修改时间来知道哪个源文件被修改了,需要重新编译。

Makefile的规则。

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

target也就是一个目标文件,可以是Object File,也可以是执行文件。还可以是一个标签(Label),对于标签这种特性,在后续的“伪目标”章节中会有叙述[3]。

prerequisites就是,要生成那个target所需要的文件或是目标。

command也就是make需要执行的命令。(任意的Shell命令)

这是一个文件的依赖关系,也就是说,target这一个或多个的目标文件依赖于prerequisites中的文件,其生成规则定义在command中。说白一点就是说,prerequisites中如果有一个以上的文件比target文件要新的话,command所定义的命令就会被执行。这就是Makefile的规则。也就是Makefile中最核心的内容。

还是以上面hello.c为例,makefile可以这样写:

# this is  make file line comment
hello:max.o min.o hello.c
  gcc max.o min.o hello.c -o hello
max.o:max.c
  gcc -c max.c
min.o:min.c
  gcc -c min.c

在定义好依赖关系后,后续的那一行定义了如何生成目标文件的操作系统命令,一定要以一个Tab键作为开头。记住,make并不管命令是怎么工作的,他只管执行所定义的命令。make会比较targets文件和prerequisites文件的修改日期,如果prerequisites文件的日期要比targets文件的日期要新,或者target不存在的话,那么,make就会执行后续定义的命令[3]。

只需要在该目录下输入make命令即可运行:

fsj@ubuntu:~/templates/makeeg$ make
gcc -c max.c
gcc -c min.c
gcc max.o min.o hello.c -o hello
fsj@ubuntu:~/templates/makeeg$ ls
hello  hello.c  makefile  max.c  max.o  min.c  min.o
fsj@ubuntu:~/templates/makeeg$ ./hello 
Min value is 22
Max value is 33

我们可以看到make程序还会输出具体command的执行过程。只需要一个命令,就生成了可执行文件,方便了不少。

那make是如何工作的呢?我们只需要输入make,make工具就会在当前目录下找名字叫“Makefile”或“makefile”的文件。 找到文件中的第一个目标文件(target),这便是最终要生成的文件。

首先查找hello的三个依赖项,对于依赖项中的.c/.cc./.cpp/.h等源码文件,不存在就直接报错。 例如修改一下hello.c的后缀:

fsj@ubuntu:~/templates/makeeg$ make
make: *** No rule to make target `hello.cc', needed by `hello'.  Stop.

对于.o目标文件,向下递归寻找其依赖项并生成。

直到所有依赖项都准备完毕,开始把下一行的command交给shell去执行。make的工作就做完了,至于你command写的对不对就和make无关了。

2.1 $ make默认target一定是第一个target

target和源码之间的依赖好比数据结构中的树,第一个target就是root节点。 如果我们写多个无关target,就相当于多了几个root节点,从第一个root并不依赖其他root,所以make程序根本不会执行到多余的target。

hello:max.o min.o hello.c
  gcc max.o min.o hello.c -o hello
hello1:max.o min.o hello.c
  gcc max.o min.o hello.c -o hello1
max.o:max.c
  gcc -c max.c
min.o:min.c
  gcc -c min.c

make之后可以看到并没有生成hello1。

没有被第一个目标文件直接或间接关联,那么它后面所定义的命令将不会被自动执行,不过,我们可以在make后面加target名字来手动指定默认tareget:$ make hello1

如果你乐意的话写多行command,生成hello1也行。

hello:max.o min.o hello.c
  gcc max.o min.o hello.c -o hello
  gcc max.o min.o hello.c -o hello1
max.o:max.c
  gcc -c max.c
min.o:min.c
  gcc -c min.c

多行command,比如command1和command2先后执行。如果command1报错,command2就不会执行了。

hello:max.o min.o hello.c
  gccc max.o min.o hello.c -o hello # error command
  gcc max.o min.o hello.c -o hello1
max.o:max.c
  gcc -c max.c
min.o:min.c
  gcc -c min.c

可以在error command前面加一个-,这样make就会忽略这个错误,继续执行下去。

2.2 一行有多个target

hello:max.o min.o hello.c
  gcc max.o min.o hello.c -o hello
min.o max.o:max.c min.c
  gcc -c max.c
  gcc -c min.c

2.3 make clean

我们定义clean这个target以此来清除所有的目标文件,以便重编译。

clean:
  rm *.o hello

类似的,手动指定target即可。

fsj@ubuntu:~/templates/makeeg$ ls
hello  hello.c  makefile  max.c  max.o  min.c  min.o
fsj@ubuntu:~/templates/makeeg$ make clean 
rm *.o hello
fsj@ubuntu:~/templates/makeeg$ ls
hello.c  makefile  max.c  min.c

Summary

  • makefile中默认生成哪个目标文件:第一个
  • target名字和gcc -o出来的不一样:以实际为准
  • 多个target生成哪个:第一个
    • 多条命令生成多个target:可以
    • 某一行报错怎么办:加-
  • 一行有多个target:可以

References

© 著作权归作者所有

realsa

realsa

粉丝 33
博文 84
码字总数 107087
作品 0
广州
程序员
私信 提问
加载中

评论(1)

乙知
乙知
一直没有静下来好好系统学一下 赞
Makefile学习入门

1、学习时遇到的问题 在学习Makefile时,首先是对其的语法不太了解,造成对项目的Makefile文件阅读很吃力;其次是在寻找资源上,着实的浪费了很大的精力;最后就是对makefile的工作原理一知半...

沙米笔记
2016/06/18
796
0
项目部署,环境搭建(pip/ makefile)

1 python包管理--pip pip search 你想要的包 # 查询相关包 pip install 包名 [==版本号]; # 指定版本可选 pip freeze >requires.txt # 导出当前环境下的配置列表 pip install -r requires.tx...

paul2011
2016/03/03
83
0
Xcode实战入门 —— [基础知识储备]

前一篇提到:你应该有一些C或者C++基础,或者Java基础。 个人以为只有Java基础入门Xcode比较困难,特别是非科班出身,从未接触过C的朋友,即使已经能照着例子写东西,确没意思到自己还在门外...

summer
2013/03/14
3.8K
2
MakeFile 入门

MakeFile是个很不错的编译管理工具, 现在发现好的工具必然是这样的, 它能提供一个细分的方法,把一个大的目标让你细化, 最后汇成总目标。 所有的软件工程必须具备这样的条理和逻辑思维,否...

晨曦之光
2012/03/09
117
0
内核模块编译 makefile入门

转自http://blog.csdn.net/zqixiao_09/article/details/50838043 一、模块的编译 我们在前面内核编译中驱动移植那块,讲到驱动编译分为静态编译和动态编译;静态编译即为将驱动直接编译进内核...

u012142460
2017/12/19
0
0

没有更多内容

加载失败,请刷新页面

加载更多

计算机实现原理专题--二进制减法器(二)

在计算机实现原理专题--二进制减法器(一)中说明了基本原理,现准备说明如何来实现。 首先第一步255-b运算相当于对b进行按位取反,因此可将8个非门组成如下图的形式: 由于每次做减法时,我...

FAT_mt
今天
4
0
好程序员大数据学习路线分享函数+map映射+元祖

好程序员大数据学习路线分享函数+map映射+元祖,大数据各个平台上的语言实现 hadoop 由java实现,2003年至今,三大块:数据处理,数据存储,数据计算 存储: hbase --> 数据成表 处理: hive --> 数...

好程序员官方
今天
7
0
tabel 中含有复选框的列 数据理解

1、el-ui中实现某一列为复选框 实现多选非常简单: 手动添加一个el-table-column,设type属性为selction即可; 2、@selection-change事件:选项发生勾选状态变化时触发该事件 <el-table @sel...

everthing
今天
6
0
【技术分享】TestFlight测试的流程文档

上架基本需求资料 1、苹果开发者账号(如还没账号先申请-苹果开发者账号申请教程) 2、开发好的APP 通过本篇教程,可以学习到ios证书申请和打包ipa上传到appstoreconnect.apple.com进行TestF...

qtb999
今天
10
0
再见 Spring Boot 1.X,Spring Boot 2.X 走向舞台中心

2019年8月6日,Spring 官方在其博客宣布,Spring Boot 1.x 停止维护,Spring Boot 1.x 生命周期正式结束。 其实早在2018年7月30号,Spring 官方就已经在博客进行过预告,Spring Boot 1.X 将维...

Java技术剑
今天
18
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部