精选文章

Android下使用TCPDUMP抓包Wireshark分析数据 如果想分析Android下某个APP的网络数据交互,需要在Android手机上抓包,最常用的抓包工具非tcpdump莫属,用tcpdump生成Wireshark识别的pcap文件,然后将pcap文件下载到电脑上,用电脑上的Wireshark加载pcap文件,通过Wireshark分析tcpdump抓取的数据。...

继续阅读

Mac下部署Android开发环境附加NDK 作为开发者,我们深有体会,不管是进行什么开发,为了部署开发环境,我们往往需要折腾很长时间、查阅很多资料才能完成,而且这次折腾完了,下次到了另一台新电脑上又得重新来过,整个部署过程记得还好,要是不记得又得重新开始,而且遇到Android这种GFW阻隔了开发资源下载链接的环境部署,又尤其浪费时间。所以这也是我写下这篇教程的初衷跟动力源泉,希望大家参考了这篇教程以后可以轻轻松松在Mac系统下将Android环境部署好。...

继续阅读

稍顯嚴肅的台中 坦白說,留在腦海中的台中影像並不多,來台灣之前在Booking上只訂到了台中的一家青旅,第一次住青旅有些不習慣,幹什麼都放不開。 同屋的一個男生是台灣人,不過一年中四分之三的時間在上海跟北京,這麼說來跟我還是比較有共同話題的。得之我準備花15天的時間環島,覺得太倉促了,他們大學時期花一個半月的時間也不見得能將台灣島給逛完。我只能無奈地表示,兩岸允許的簽證時間有限,自己的空閒時間更有限,只能用打卡式的旅行了,我深知正真地旅行應該慢下來,融入當地的環境,感受他們的風土人情,但第一次只能這樣作罷,以後換成民進黨上台,形勢會變成怎樣還不得而知,能否再過來還是個未知數。而我一向信奉的人生格言是秉燭夜遊,活在當下,所以,理解自己吧。...

继续阅读

為之留戀的新竹 來新竹之前本沒有對她有過高的期待,慢慢對她加分要從桃園火車站出發前往新竹開始。 在桃園火車站的候車月台上,有醒目的旅遊資料發放處,這上面的擺放的全是新竹的旅遊宣傳資料,關鍵的是資料做得非常簡潔易懂,而接下來一天的新竹之行就全部是依據這份寶典的指引來完成的。...

继续阅读

從桃園開始台灣之行 初到台灣恰逢華夏銀行系統升級,特意準備的華夏銀聯卡在桃園機場沒能派上用場,只好用建行在機場5000塊,算下來是很不划算的,但是沒辦法,誰叫我出機場就得花錢呢。 從機場打車到桃園的酒店,花了將近六百塊新台幣,到酒店時五點多,天已經漸亮了,洗漱完等到七點吃過早餐就開始補覺囉,一覺醒來已是中午,帶著換下來的衣服外出找自助洗衣店,順便覓食。...

继续阅读

  • Prev
  • Next

Linux下的Make与Makefile

2

文章分类 : C++, C语言, Linux, Windows, 应用与编程, 教程

对于 Linux 跟 Unix 系统而言,make 是一个极其重要的编译命令,我们在开发项目或者安装应用软件时,经常要用到 make 或 make install,对于一个包含几十、几百甚至成千上万个源文件的项目,如果每次都要键入 gcc 或 g++ 等命令来进行编译的话,那对于程序员简直就是一场噩梦,而使用 make 和 makefile 工具便可以简洁明了地理顺各个源文件之间纷繁复杂的相互关系,将大型项目分解成多个更易于管理的模块,自动完成编译工作,并且可以只对程序员上次编译后修改过的部分进行编译。

因此,有效的利用 make 和 makefile 工具可以大大提高程序开发的效率。同时也极大地减轻了 Linux 下应用程序安装的难度。接下来,就让我们来详细了解一下 make 及其描述文件 makefile。

一、make

1、程序的诞生

无论是 C语言 还是 C++,我们通常首先将源文件编译成中间代码,在 Windows 下是“.obj”文件,在 Linux 或 Unix 下是“.o”文件,即目标文件(Object File),这个过程叫作编译(Compile);然后再把生成的目标文件合成可执行文件,这个过程叫作链接(Link)。

编译时,编译器负责检查语法、函数与变量的声明正确与否,只要检测过关,就将生成目标文件,一般而言,每个源文件都对应一个目标文件。

链接时,编译器负责链接函数和全局变量,编译器并不关心函数所在的源文件,只关心目标文件,有时候因为源文件太多,编译时生成的目标文件很多,导致在链接时需要指定一大堆的目标文件,显然很不方便,这时候我们可以将目标文件打包成一个库文件,Windows 下这种库文件也就是“.lib”文件,Linux 下是“.a”或“.so”文件。

总之,编译时,我们通常先将源文件编译成目标文件,再对目标文件进行链接操作,程序便诞生了。

2、make 的工作原理

make 工具最基本的功能是调用 Makefile 文件,通过 Makefile 文件来描述程编译的整个过程,不必每次敲 gcc 或 g++ 等命令来完成编译工作。当然,Makefile 文件需要按照一定的语法进行编写,说明如何编译各个源文件并链接生成可执行文件,以及各个文件之间的依赖关系。

如下图所示,演示了一个简单的 make 命令编译过程:

trevor@trevor-PC:~/linux/linux100$ ls
add.c main.c Makefile
trevor@trevor-PC:~/linux/linux100$ cat Makefile
#This is a example for describing makefile
add:main.o add.o
gcc -o add main.o add.o
main.o:main.c
gcc -c main.c
add.o:add.c
gcc -c add.c

clean:
rm main.o add.o add
trevor@trevor-PC:~/linux/linux100$ make
gcc -c main.c
gcc -c add.c
gcc -o add main.o add.o
trevor@trevor-PC:~/linux/linux100$ ls
add add.c add.o main.c main.o Makefile
trevor@trevor-PC:~/linux/linux100$ ./add 12 17
12 + 17 = 29
trevor@trevor-PC:~/linux/linux100$ make clean
rm main.o add.o add
trevor@trevor-PC:~/linux/linux100$ ls
add.c main.c Makefile
trevor@trevor-PC:~/linux/linux100$

上图中,当前目录下有一个名为 Makefile 的文件,其内容如下:

#This is a example for describing makefile
add:main.o add.o
gcc -o add main.o add.o
main.o:main.c
gcc -c main.c
add.o:add.c
gcc -c add.c

clean:
rm main.o add.o add

在这个 Makefile 文件中,第一行第一个字符为 #,这是一个注释行;第二行指定可执行文件 add 依赖目标文件 main.o、add.o;第三行表述了第二行的具体依赖关系,即执行命令“gcc -o add main.o add.o”,将目标文件链接成为可执行文件;第四行指定目标文件 main.o 依赖源文件 main.c;第五行表述第四行的具体依赖关系,即执行命令“gcc -c main.c”,将源文件 main.c 编译成目标文件 main.o;第六行指定目标文件 add.o 依赖源文件 add.c;第七行表述第六行的具体依赖关系,即执行命令“gcc -c add.c”,将源文件 add.c 编译成目标文件 add.o。

在当前目录下输入 make 命令,系统将自动完成如下操作。

(1) make 工具在当前目录下依次寻找名为 GNUmakefile、makefile 或 Makefile 的文件,找到一个则停止查找;
(2) 如果找到,它会查找文件中的第一个目标,如上面例子中的 add,并将这个文件作为最后一步生成的目标;
(3) 如果 add 文件不存在,或是 add 所依赖的后面的“.o”文件的修改时间比 add 文件晚,那么系统就会执行后面所定义的命令来生成这个 add 文件;
(4) 如果 add 所依赖的“.o”文件也不存在,那么 make 工具就会在当前文件中查找目标为“.o”文件依赖性,如果找到则根据相应的规则生成“.o”文件;
(5) 如果 makefile 文件中列出的源文件都存在,make 工具就会先生成“.o”文件,然后再用“.o”文件链接成可执行文件,否则将提示“找不到目标”错误。

make 会一层一层地去解析文件的依赖关系,一步步地编译出各个目标,直到最终编译出第一个目标文件。在解析的过程中,如果出现错误,比如最后被依赖的文件没有找到,make 工具就会直接退出并报错。而对于每条依赖后面所定义的命令的错误,make 工具不会检查。

通常,makefile 文件中还定义有 clean 目标,这是一个伪目标,可用来清除编译过程中生成的中间文件,例如清除上例中的内容:

clean :
rm main.o add.o add

在上述 makefile 文件中,clean 没有被第一个目标 add 直接或间接依赖,那么它后面所定义的命令就不会被自动执行。不过,可以在 make 命令后跟 clean 目标作为参数来执行其后所定义的命令,即执行“make clean”命令,用于清除所有 make 过程中生成的目标,以便重新编译。

3、make 的语法及参数选项

make 命令主要有标志、宏定义和目标名三个可选参数。其标准形式为:

make [标志] [宏定义] [目标名]

主要标志选项及其含义如下表所示。

-f FILE:读取 FILE 文件作为一个 makefile.。
-i:忽略命令执行返回的出错信息。
-s:沉默模式,在执行之前不输出相应的命令行信息。
-r:禁用内置隐含规则。
-n:非执行模式,输出所有执行命令,但并不执行。
-t:使用 touch 命令创建目标而不是根据依赖后面定义的命令来生成目标。
-q:根据目标文件是否已经更新返回 0 或非 0 的状态信息。
-p:输出所有宏定义和目标文件描述。
-d:Debug模式,输出有关文件和检测时间的详细信息。
-C dir:在所有操作前切换到 dir 目录。
-I dir:包含其他 makefile 文件时,利用该选项指定搜索目录。
-h:打印帮助信息。
-w:在处理 makefile 之前和之后,都显示 makefile 所在目录。

宏定义选项主要用于为 makefile 中已经定义的宏变量赋值,比如在下面这个 makfile 文件中,宏变量 CC 未定义。

#This is a example for describing makefilie
CC=

add:main.o add.o
$(CC) -o add main.o add.o
main.o:main.c
$(CC) -c main.c
add.o:add.c
$(CC) -c add.c

clean:
rm main.o add.o add

执行 make 命令时,我们可以通过参数为 makefile 文件内的宏变量 CC 赋值,通过宏变量 CC 来指定不同的编译器来编译源文件,如下图所示。

trevor@trevor-PC:~/linux/linux100$ ls
add.c GNUmakefile main.c makefile
trevor@trevor-PC:~/linux/linux100$ make CC=gcc
gcc -c main.c
gcc -c add.c
gcc -o add main.o add.o
trevor@trevor-PC:~/linux/linux100$ ls
add add.c add.o GNUmakefile main.c main.o makefile
trevor@trevor-PC:~/linux/linux100$ ./add 55 66
55 + 66 = 121
trevor@trevor-PC:~/linux/linux100$ make clean
rm main.o add.o add
trevor@trevor-PC:~/linux/linux100$ ls
add.c GNUmakefile main.c makefile
trevor@trevor-PC:~/linux/linux100$ make CC=g++
g++ -c main.c
g++ -c add.c
g++ -o add main.o add.o
trevor@trevor-PC:~/linux/linux100$ ls
add add.c add.o GNUmakefile main.c main.o makefile
trevor@trevor-PC:~/linux/linux100$ ./add 77 88
77 + 88 = 165
trevor@trevor-PC:~/linux/linux100$

更严格的写法应该为宏定义选项将上双引号,即“CC=gcc”,尤其是在宏定义的右值有空格时。

目标名选项用来指定 make 命令要编译或执行的目标,并且允许同时指定多个目标,如上例中的“make clean”。操作时按照从左到右的顺序依次编译或执行各个目标。如果不指定目标名选项,则系统默认指向 makefile 文件中的第一个目标,如上例中的 add。

二、makfile

1、剖析 makefile

Makefile 是 make 命令依赖并读取的配置文件,它描述了编译整个项目的详细规则,为了让 make 命令得以识别,makefile 文件遵循一定的格式,它通常包含如下内容:

需要由 make 命令创建的目标对象(targets),通常是目标文件或者可执行文件;
要创建的目标对象所依赖的文件(dependent_files);
创建每个目标对象时需要运行的命令(command)。

它的格式为:

targets …:dependent_files …
(tab)command
…

例如,有两个文件分别为 hello.c 和 hello.h,创建的目标体为 hello.o,执行的命令为 gcc 编译指令“gcc –c hello.c”,那么对应的 Makefile 就可以写为:

#This is a example for describing makefile
hello.o: hello.c hello.h //要创建的目标对象所偏依赖的文件
gcc -c hello.c -o hello.o //创建目标对象要运行的命令

注意,在 Makefile 中的每个 command 前必须有制表符 tab,否则在运行 make 命令时会出错。在 Makefile 文件所在目录执行 make 命令,使用 make 命令的格式为:make target,这样 make 命令就会自动解析 Makefile 文件并搜寻指定 target 后面的依赖文件 dependent_files,当且仅当依赖文件都存在时才执行后面的 command 语句,否则需要先生成依赖文件方可。如下图所示。

trevor@trevor-PC:~/linux/linux100$ ls
hello.c hello.h Makefile
trevor@trevor-PC:~/linux/linux100$ cat Makefile
#This is a example for describing makefile
hello.o: hello.c hello.h
gcc -c hello.c -o hello.o
trevor@trevor-PC:~/linux/linux100$ make hello.o
gcc -c hello.c -o hello.o
trevor@trevor-PC:~/linux/linux100$ ls
hello.c hello.h hello.o Makefile
trevor@trevor-PC:~/linux/linux100$

可以看到,make 命令执行了 Makefile 文件中目标对象 hello.o 对应的命令语句,并生成了目标对象 hello.o。

2、说说makefile的文件名

默认情况下,make 命令会在当前目录下按顺序寻找文件名为 GNUMakefile、makefile、Makefile 的文件,倘若找到一个则停止往后继续寻找,如果都未找到,则提示类似于“没有指明目标并且找不到 makefile”的错误。在这个三个文件名中,最好是用 Makefile 这个文件名,因为这个名字首字母大写比较醒目,最好不要使用 GNUMakefile 作为文件名,因为它只被 GNU 的 make 识别,还有一些 make 只对首字母小写的 makefile 文件敏感,但是通常来说,大多数的 make 都支持 Maklefile 跟 makefile 这两个默认文件名。

我们也可以使用别的名字来命名 makefile 文件,比如“make.mips”、“make.arm”、“make.android”等,为了让 make 命令可以识别这些特殊的文件名,在执行 make 命令时需要加上“-f”参数,如:make -f make.mips。

3、makefile的包含

跟 C/C++ 的“#include”一样,makefile 文件可以使用 include 关键字将其他 makefile 包含进来,被包含的文件内容会原封不动地加载到当前 makefile 文件的包含位置。include 关键词前面可以有一些空格字符,但是绝对不能以制表符 tab 键,因为制表符在 makefile 文件内通常用来标识一个命令的开始。include 关键词后面指定要包含的文件名、路径或者变量,它们之间用一个或者多个空格隔开,举例来说,倘若你有几个 makefile:a.make、b.make、c.make,还有一个 makefile 相关文件(定义一些宏变量或者宏变量的文件) make.rules,以及一个变量 $(include_dir),那么下面语句:

include_dir:=/usr/include
include *.make $(include_dir)make.rules

等价于:

include a.make b.make c.make /usr/include/make.rules

make 命令执行时,先将 include 关键字后面指定的相关文件内容加载进来并安置在当前位置,如果文件未指定绝对路径,make 命令将先在当前目录寻找,倘若未找到,make 命令一般还会去下面几个目录寻找:

(1) 如果执行 make 命令时使用“-I”参数指定了某个目录,make 命令会去该目下寻找相关文件;
(2) 如果 /usr/local/bin 或 /usr/include 目录存在,make 命令将去该目录下寻找相关文件。

make 命令如果未找到 include 关键字指定的某个文件,将警告“没有那个文件或目录”等错误而停止运行,如果想让 make 命令不去理会这些相关文件是否存在而继续执行,可以在 include 前面加上一个减号“-”,它表示无论 include 加载过程中出现的错误都被忽略。

除非注明,文章均为CppLive 编程在线原创,转载请注明出处,谢谢。

本文地址:https://www.cpplive.com/html/1776.html

评论 (2)

  • Adoo says:

    赞!印象中你似乎写了不少关于Makefile的文章,我前段时间突然看了一点点Cmake,很强大,感觉Cmake这样的构建方式,还是比较先进的。不过我没有时间深究它,写makefile我也不在行,只是看到你在这方面有兴趣,有研究,推荐你看看。

  • 这里因为你的留言而存在!!!

    You must be logged in to post a comment.