Makefile 是什么?GNU下的make详解

以前在windows上编程的时候,从来也没有见过makefile文件,也根本没有意识到这个东西。直到用了Linux虚拟机进行一些课程作业的时候我才发现还有这么个东西存在着。那么问题来了,它是什么?怎么用?

一个工程中的源文件不计其数,其按类型、功能、模块分别放在若干个目录中,makefile 定义了一系列的规则来指定哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为 makefile 就像一个 Shell 脚本一样,也可以执行操作系统的命令。

也就是说,makefile 的作用就是设定整个工程文件的编译规则,能实现 “自动化编译”。用简单唯一的 “make” 命令去一键完成所有复杂的编译命令。
但是为什么在 Windosw 上好像很少见到这种东西呢?因为强大的 IDE 帮助程序员完成了这个工作。但是要是想要具备各种情况下开发大型项目的能力以及在 Linux 环境下写大型工程,还是一定得掌握 Makefile 的具体编写方法的。

前置知识:可执行文件生成的步骤

可执行文件是如何产生的?

  1. 预处理:删除 #define 并展开宏定义/处理条件预编译指令/删除所有注释/添加行号等标识(可读)
  2. 编译:分析语义,生成汇编代码(可读)
  3. 汇编:将汇编代码转换成机器码指令序列(不可读)
  4. 链接:将多个可重定位目标文件合并,生成最终的可执行文件

其中,只要代码的语法正确,声明均正确,这个时候只要提供给编译器头文件的位置,就可以给一个源文件生成一个对应的中间目标文件,后缀一般为 .o 或 .obj
在链接过程中,由于文件的数量可能非常巨大,并且文件夹层次太多。这个时候就需要给这些中间目标文件打包,后缀为 .lib 或 .a

GCC 编译命令

毕竟 makefile 就是代替我们手动执行编译命令的,里面的内容当然也是以编译命令为主。由于这个博客的起因就是 Linux 上的 makefile,那么整篇博客的编译命令和 makefile 编写方法都是基于 GNU 的了。至于 Linux 、GNU 、GCC 这一堆东西有什么关系在这里就不详细说明了。(不过它们的关系还是挺有意思的喔
正如上一个标题下说的内容一样,GCC 的编译过程分为四个阶段,每个阶段的文件后缀可能有所不同,在这里列一个小小的表格:

后缀描述
.cC源文件
.C/.cc/.cxx/.cppC++源文件
.hC/C++头文件
.i/.ii经过预处理后
.s/.S经过编译后 汇编代码
.o/.obj经过汇编后 中间目标文件
.a/.lib静态链接库
.so/.dll动态链接库

而 GCC 的编译命令非常简单,只有一句(核心只有一句

gcc [options] file…

如果不加任何 option,默认的操作是输出为 .out
而每个 GCC 编译命令的区别就是 options 的不同了。options 可替换的选项非常非常多,我简单归类一些放在下面:

  • -E :只执行到预处理
  • -s :只执行到编译
  • -c :只执行到汇编
  • -o :指定输出文件(例如:gcc hello.o -o hello)
  • -pie :创建一个动态链接、位置无关的可执行文件
  • -I :指定头文件的包含路径
  • -L :指定链接库的包含路径
  • -shared :创建共享库/动态库
  • -static :使用静态链接
  • –help :显示帮助信息
  • –version :显示编译器版本
  • -Wa, :将逗号分隔的 options 传递给汇编器
  • -Wp, :传递给预处理器
  • -Wl, :传递给链接器
    此外还有一些特殊的命令,如下:
  • ar rcs xxxx.a file… :由file… 生成静态链接库xxxx.a
  • -rpath=’xxx/xxx’ :与-Wl搭配使用,将动态链接库的位置嵌入程序之中
    也可以通过将动态链接库放入系统路径或是将路径添加到环境变量 LD_LIBRARY_PATH中来使用动态链接库

makefile 的编写方法

一个强力的 makefile 文件,不能仅满足于最最基本的编译功能。简单设计一下,一个工程的 makefile 应该能实现这样的功能:

  1. 如果工程没有被编译过,那么进行编译和链接
  2. 如果工程的某些文件被修改了,那么重新编译相关的文件,并且链接

这样看来,是不是就和 Windows 的 IDE 里面的编译功能差不多了?

makefile 基本语法

target … : prerequisites …
    command

这就是 makefile 的基本语法。其中 target 是要生成的目标文件或者可执行文件,prerequisites 是生成该 target 时所依赖的文件。
在使用 make 命令之后,系统会判断有没有产生过该目标文件,如果没有就执行 command;如果产生过,那么依赖文件中有没有哪些在上一次编译后又进行了修改,如果有就执行 command
特别注意的一点是:command 每一行的开头必须是一个 Tab,而不是空格

Tips

隐含规则

在 makefile 的编写当中,一些约定俗成的规则就可以省略掉,如:在 C 语言编译的过程中,.o 文件往往依赖于 .c 文件,那么就可以省略该条语句下面的 command

hello.o : hello.c
    gcc -c hello.c

#可以写作
hello.o : hello.c

使用变量

有时,我们需要写出大量重复的依赖,这样容易产生错误。且如果有新增的依赖文件的话,可能会在某些地方漏掉。这个时候可以制作一个变量,把这些可以分成一组的依赖文件添加到其中去,就可以避免在各个地方重复每一个名字了。如:

hello : hello.o print.o insert.o search.o
    cc -o hello hello.o print.o insert.o search.o

#可以写作
objects = hello.o print.o insert.o search.o
hello : $(objects)
    cc -o hello $(objects)

同样,在很多个文件都依赖于同一个文件的话,也可以使用变量。如:

$(objects) : files.h

当然,任何一个操作都可能有弊端,这样处理的结果是,文件的依赖关系可能会变得不明确,所以到底怎么样写可能还要取决于不同人对于代码格式的要求。

伪目标

如果想要清除所有生成的 object 文件和执行文件,有的 makefile 可以使用 make clean 指令,这就是通过伪目标实现的,代码如下:

.PHONY: clean
clean:
    -rm edit $(objects)

其中,.PHONY 的意思就是声明这个叫做 clean 的并不是一个真的目标,所以避免 make 的时候把它当作一个目标文件去执行。
按照这样的方法,我们还可以写出其他需要的命令。
但是有一个 约定俗成 的事情,就是 make clean 一般都会在 makefile 文件的最后去实现。

梳理

makefile 是一种处理大型工程的编译的文件,在其中需要按照顺序列出想要生成的目标和依赖文件,以及想要对它们使用的编译命令。
在使用 make 命令运行它之后,make 会把文件中的第一个目标当成默认目标,以生成它为最终的文件。首先它会去寻找生成默认目标的依赖文件,如果并没有找到文件,它就会在 makefile 之中寻找有没有以该依赖文件为目标的代码,进而去查看它的依赖文件……直到找到了最底层的依赖文件,make 就会开始一步一步完成编译指令。
在这个过程中,如果有依赖文件完全缺失、任意地方语法错误等情况,make 都会直接停止。
最后,提供一个 makefile 文件的例子:

objects = main.o hello.o display.o buff.o
hello : $(objects)
    cc -o hello $(objects)

$(objects) : hello.h
hello.o : hello.c
display.o :display.c display.h
buff.o : buff.c

.PHONY: clean
clean:
    -rm hello $(object)

其他文章推荐:

操作系统 伙伴算法和Slab分配器-HNU操作系统课堂讨论 – iLoner的博客 (iloner121.com)

首页 » Makefile 是什么?GNU下的make详解
资料整理不易,感兴趣的话请持续关注。欢迎互相添加友联

评论

  1. 远小远
    7月前
    2022-5-02 2:08:45

    虽然暂时看不懂,或许我也会有学到的一天,先留下我的足迹吧!我要把这个分享给我那学电脑的朋友!

发送评论 编辑评论

|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇