linux中的make命令解释集锦
Posted 默一鸣
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了linux中的make命令解释集锦相关的知识,希望对你有一定的参考价值。
无论是在Linux还是在Unix环境中,make都是一个非常重要的编译命令。不管是自己进行项目开发还是安装应用软件,我们都经常要用到make或make install。利用make工具,我们可以将大型的开发项目分解成为多个更易于管理的模块,对于一个包括几百个源文件的应用程序,使用make和 makefile工具就可以简洁明快地理顺各个源文件之间纷繁复杂的相互关系。而且如此多的源文件,如果每次都要键入gcc命令进行编译的话,那对程序员来说简直就是一场灾难。
而make工具则可自动完成编译工作,并且可以只对程序员在上次编译后修改过的部分进行编译。因此,有效的利用make和 makefile工具可以大大提高项目开发的效率。同时掌握make和makefile之后,您也不会再面对着Linux下的应用软件手足无措了。
但令人遗憾的是,在许多讲述Linux应用的书籍上都没有详细介绍这个功能强大但又非常复杂的编译工具。在这里我就向大家详细介绍一下make及其描述文件makefile。
Makefile文件
Make工具最主要也是最基本的功能就是通过makefile文件来描述源程序之间的相互关系并自动维护编译工作。而makefile 文件需要按照某种语法进行编写,文件中需要说明如何编译各个源文件并连接生成可执行文件,并要求定义源文件之间的依赖关系。makefile
文件是许多编译器--包括 Windows NT 下的编译器--维护编译信息的常用方法,只是在集成开发环境中,用户通过友好的界面修改 makefile 文件而已。
在 UNIX 系统中,习惯使用 Makefile 作为 makfile 文件。如果要使用其他文件作为 makefile,则可利用类似下面的 make 命令选项指定 makefile 文件:
$ make -f Makefile.debug
例如,一个名为prog的程序由三个C源文件filea.c、fileb.c和filec.c以及库文件LS编译生成,这三个文件还分别包含自己的头文件a.h 、b.h和c.h。通常情况下,C编译器将会输出三个目标文件filea.o、fileb.o和filec.o。假设filea.c和fileb.c都要声明用到一个名为defs的文件,但filec.c不用。即在filea.c和fileb.c里都有这样的声明:
#include "defs"
那么下面的文档就描述了这些文件之间的相互联系:
---------------------------------------------------------
#It is a example for describing makefile
prog : filea.o fileb.o filec.o
cc filea.o fileb.o filec.o -LS -o prog
filea.o : filea.c a.h defs
cc -c filea.c
fileb.o : fileb.c b.h defs
cc -c fileb.c
filec.o : filec.c c.h
cc -c filec.c
----------------------------------------------------------
这个描述文档就是一个简单的makefile文件。
从上面的例子注意到,第一个字符为 # 的行为注释行。第一个非注释行指定prog由三个目标文件filea.o、fileb.o和filec.o链接生成。第三行描述了如何从prog所依赖的文件建立可执行文件。接下来的4、6、8行分别指定三个目标文件,以及它们所依赖的.c和.h文件以及defs文件。而5、7、9行则指定了如何从目标所依赖的文件建立目标。
当filea.c或a.h文件在编译之后又被修改,则 make 工具可自动重新编译filea.o,如果在前后两次编译之间,filea.C 和a.h 均没有被修改,而且 test.o 还存在的话,就没有必要重新编译。这种依赖关系在多源文件的程序编译中尤其重要。通过这种依赖关系的定义,make 工具可避免许多不必要的编译工作。当然,利用 Shell
脚本也可以达到自动编译的效果,但是,Shell 脚本将全部编译任何源文件,包括那些不必要重新编译的源文件,而 make 工具则可根据目标上一次编译的时间和目标所依赖的源文件的更新时间而自动判断应当编译哪个源文件。
Makefile文件作为一种描述文档一般需要包含以下内容:
◆ 宏定义
◆ 源文件之间的相互依赖关系
◆ 可执行的命令
Makefile中允许使用简单的宏指代源文件及其相关编译信息,在Linux中也称宏为变量。在引用宏时只需在变量前加$符号,但值得注意的是,如果变量名的长度超过一个字符,在引用时就必须加圆括号()。
下面都是有效的宏引用:
$(CFLAGS)
$2
$Z
$(Z)
其中最后两个引用是完全一致的。
需要注意的是一些宏的预定义变量,在Unix系统中,$*、[email protected]、$?和$1zap
/usr/bin/make -dp | grep -v TIME>2zap
diff 1zap 2zap
rm 1zap 2zap
lint: dosys.c donamc.c file.c main.c misc.c version.c gram.c
$(LINT) dosys.c donamc.c file.c main.c misc.c version.c
gram.c
rm gram.c
arch:
ar uv /sys/source/s2/make.a $(FILES)
----------------------------------------------------------
通常在描述文件中应象上面一样定义要求输出将要执行的命令。在执行了make命令之后,输出结果为:
$ make
cc -c version.c
cc -c main.c
cc -c donamc.c
cc -c misc.c
cc -c file.c
cc -c dosys.c
yacc gram.y
mv y.tab.c gram.c
cc -c gram.c
cc version.o main.o donamc.o misc.o file.o dosys.o gram.o
-LS -o make
13188+3348+3044=19580b=046174b
最后的数字信息是执行"@size make"命令的输出结果。之所以只有输出结果而没有相应的命令行,是因为"@size make"命令以"@"起始,这个符号禁止打印输出它所在的命令行。
描述文件中的最后几条命令行在维护编译信息方面非常有用。其中"print"命令行的作用是打印输出在执行过上次"make print"命令后所有改动过的文件名称。系统使用一个名为print的0字节文件来确定执行print命令的具体时间,而宏$?则指向那些在print 文件改动过之后进行修改的文件的文件名。如果想要指定执行print命令后,将输出结果送入某个指定的文件,那么就可修改P的宏定义:
make print "P= cat>zap"
在Linux中大多数软件提供的是源代码,而不是现成的可执行文件,这就要求用户根据自己系统的实际情况和自身的需要来配置、编译源程序后,软件才能使用。只有掌握了make工具,才能让我们真正享受到到Linux这个自由软件世界的带给我们无穷乐趣。
==========================================
Makefile 初探
==========================================
Linux 的内核配置文件有两个,一个是隐含的.config文件,嵌入到主Makefile中;另一个是include/linux/autoconf.h,嵌入到各个c源文件中,它们由make config、make menuconfig、make xconfig这些过程创建。
几乎所有的源文件都会通过linux/config.h而嵌入autoconf.h,如果按照通常方法建立文件依赖关系 (.depend),只要更新过autoconf.h,就会造成所有源代码的重新编绎。
为了优化make过程,减少不必要的重新编绎,Linux开发了专用的mkdep工具,用它来取代gcc来生成.depend文件。mkdep在处理源文件时,忽略linux/config.h这样的头文件,识别源文件宏指令中具有"CONFIG_"特征的行。例如,如果有"#ifdef CONFIG_SMP"这样的行,它就会在.depend文件中输出$(wildcard /usr/src/linux/include/config/smp.h)。
include/config/下的文件是另一个工具 split-include从autoconf.h中生成,它利用autoconf.h中的CONFIG_标记,生成与mkdep相对应的文件。例如,如果autoconf.h中有"#undef CONFIG_SMP"这一行,它就生成include/config/smp.h文件,内容为"#undef CONFIG_SMP"。这些文件名只在.depend文件中出现,内核源文件是不会嵌入它们的。每配置一次内核,运行split-include一次。
split-include会检查旧的子文件的内容,确定是不是要更新它们。这样,不管autoconf.h修改日期如何,只要其配置不变,make就不会重新编绎内核。
如果系统的编绎选项发生了变化,Linux也能进行增量编绎。为了做到这一点,make每编绎一个源文件时生成一个 flags文件。例如编绎sched.c时,会在相同的目录下生成隐含的.sched.o.flags文件。它是Makefile的一个片断,当make 进入某个子目录编绎时,会搜索其中的flags文件,将它们嵌入到Makefile中。这些flags代码测试当前的编绎选项与原来的是不是相同,如果相同,就将自已对应的目标文件加入FILES_FLAGS_UP_TO_DATE列表,然后,系统从编绎对象表中删除它们,得到
FILES_FLAGS_CHANGED列表,最后,将它们设为目标进行更新。
Linux 下 make 命令是系统管理员和程序员用的最频繁的命令之一。管理员用它通过命令行来编译和安装很多开源的工具,程序员用它来管理他们大型复杂的项目编译问题。本文我们将用一些实例来讨论 make 命令背后的工作机制。
Make 如何工作的
对于不知道背后机理的人来说,make 命令像命令行参数一样接收目标。这些目标通常存放在以 “Makefile” 来命名的特殊文件中,同时文件也包含与目标相对应的操作。更多信息,阅读关于 Makefiles 如何工作的系列文章。
当 make 命令第一次执行时,它扫描 Makefile 找到目标以及其依赖。如果这些依赖自身也是目标,继续为这些依赖扫描 Makefile 建立其依赖关系,然后编译它们。一旦主依赖编译之后,然后就编译主目标(这是通过 make 命令传入的)。
现在,假设你对某个源文件进行了修改,你再次执行 make 命令,它将只编译与该源文件相关的目标文件,因此,编译完最终的可执行文件节省了大量的时间。
Make 命令实例
下面是本文所使用的测试环境:
OS —— Ubunut 13.04
Shell —— Bash 4.2.45
Application —— GNU Make 3.81
下面是工程的内容:
$ ls
anotherTest.c Makefile test.c test.h
下面是 Makefile 的内容:
all: test
test: test.o anotherTest.o
gcc -Wall test.o anotherTest.o -o test
test.o: test.c
gcc -c -Wall test.c
anotherTest.o: anotherTest.c
gcc -c -Wall anotherTest.c
clean:
rm -rf *.o test
现在我们来看 Linux 下一些 make 命令应用的实例:
1. 一个简单的例子
为了编译整个工程,你可以简单的使用 make
或者在 make 命令后带上目标 all
。
$ make
gcc -c -Wall test.c
gcc -c -Wall anotherTest.c
gcc -Wall test.o anotherTest.o -o test
你能看到 make 命令第一次创建的依赖以及实际的目标。
如果你再次查看目录内容,里面多了一些 .o 文件和执行文件:
$ ls
anotherTest.c anotherTest.o Makefile test test.c test.h test.o
现在,假设你对 test.c 文件做了一些修改,重新使用 make 编译工程:
$ make
gcc -c -Wall test.c
gcc -Wall test.o anotherTest.o -o test
你可以看到只有 test.o 重新编译了,然而另一个 Test.o 没有重新编译。
现在清理所有的目标文件和可执行文件 test,你可以使用目标 clean
:
$ make clean
rm -rf *.o test
$ ls
anotherTest.c Makefile test.c test.h
你可以看到所有的 .o 文件和执行文件 test 都被删除了。
2. 通过 -B 选项让所有目标总是重新建立
到目前为止,你可能注意到 make 命令不会编译那些自从上次编译之后就没有更改的文件,但是,如果你想覆盖 make 这种默认的行为,你可以使用 -B 选项。
下面是个例子:
$ make
make: Nothing to be done for `all’.
$ make -B
gcc -c -Wall test.c
gcc -c -Wall anotherTest.c
gcc -Wall test.o anotherTest.o -o test
你可以看到尽管 make 命令不会编译任何文件,然而 make -B
会强制编译所有的目标文件以及最终的执行文件。
3. 使用 -d 选项打印调试信息
如果你想知道 make 执行时实际做了什么,使用 -d 选项。
这是一个例子:
$ make -d | more
GNU Make 3.81
Copyright (C) 2006 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
This program built for x86_64-pc-linux-gnu
Reading makefiles…
Reading makefile `Makefile’…
Updating makefiles….
Considering target file `Makefile’.
Looking for an implicit rule for `Makefile’.
Trying pattern rule with stem `Makefile’.
Trying implicit prerequisite `Makefile.o’.
Trying pattern rule with stem `Makefile’.
Trying implicit prerequisite `Makefile.c’.
Trying pattern rule with stem `Makefile’.
Trying implicit prerequisite `Makefile.cc’.
Trying pattern rule with stem `Makefile’.
Trying implicit prerequisite `Makefile.C’.
Trying pattern rule with stem `Makefile’.
Trying implicit prerequisite `Makefile.cpp’.
Trying pattern rule with stem `Makefile’.
--More--
这是很长的输出,你也看到我使用了 more
命令来一页一页显示输出。
4. 使用 -C 选项改变目录
你可以为 make 命令提供不同的目录路径,在寻找 Makefile 之前会切换目录的。
这是一个目录,假设你就在当前目录下:
$ ls
file file2 frnd frnd1.cpp log1.txt log3.txt log5.txt
file1 file name with spaces frnd1 frnd.cpp log2.txt log4.txt
但是你想运行的 make 命令的 Makefile 文件保存在 ../make-dir/ 目录下,你可以这样做:
$ make -C ../make-dir/
make: Entering directory `/home/himanshu/practice/make-dir’
make: Nothing to be done for `all’.
make: Leaving directory `/home/himanshu/practice/make-dir
你能看到 make 命令首先切到特定的目录下,在那执行,然后再切换回来。
5. 通过 -f 选项将其它文件看作 Makefile
如果你想将重命名 Makefile 文件,比如取名为 my_makefile 或者其它的名字,我们想让 make 将它也当成 Makefile,可以使用 -f 选项。
make -f my_makefile
通过这种方法,make 命令会选择扫描 my_makefile 来代替 Makefile。
一、确认已经装好了GCC和Make的软件包
可以使用whereis命令查看:
如果whereis gcc和whereis make命令有结果,说明安装了这两个软件,可以继续往下做。
二、使用GCC编译运行一个HelloWorld程序(只涉及单个文件)
可以在任何一个目录编写C程序然后编译运行,我这个实例在自己主目录进行:
然后就进入了编写程序的界面:
按下键盘”i”进入编辑界面,然后输入程序:
按ESC(进入命令行模式),然后输入”:wq”,冒号表示开始输入命令,字母w代表保存文件,字母q代表退出编辑器:
按回车退出vim编辑器,退回到终端,以下是之后的编译运行截图:
三、使用GCC编译运行一个多文件程序(包含主程序和子程序)
这里我们要写两个C程序文件,一个文件里面写个被调函数,另外一个文件中main函数调用第一个文件的函数,如下所示:
ex_display.c的代码如下,同样的写完后ESC然后输入:wq退出:
输入如下的main函数代码:
然后保存退出,如下是编译运行过程:
四、使用Makefile解决多文件编译运行的问题
正如上节的红框框里面所叙述,如果一个程序涉及的文件很多的话,每个都得写出来,很是麻烦,所以Makefile就出现了,请看教程:
进入makefile的编辑界面后,输入如下内容:
然后保存退出,运行make命令:
五、Makefile和shell script方法的对比
有人说,我把之前的所有命令,全写到shell script里面,不就达到Makefile的效果了,没错确实最终效果是相同的,但是Makefile却有这些好处:
- 简化编译执行的命令(并没有gcc –c的过程)
- 一次make后,下次只会编译改动的文件,其它的文件不会再编译了
其它还有一些优点,不过这第二个优点,对于大型项目来说,好处太大了!
以上是关于linux中的make命令解释集锦的主要内容,如果未能解决你的问题,请参考以下文章