6、GNU makefile工程管理学习的一个例子
在之前我们已经学习了一个文件的编译过程,但是做过项目的都知道,一个工程中的源文件不计其数,其按类型、功能、模块会分别放在若干个目录中,而这些文件如何编译就需要有一个编译规则,虽然现在很多大型的项目都是直接用诸如CMake、Autotools、SCons等工具生成的,但是对于一些小项目,使用工具就没有什么必要了,而且对于项目编写来说,掌握Makefile的基本知识对我们来说也是很有必要的。
我们本篇文章所要讲解的就是这个编译规则的文件makefile的规则及如何进行编写。makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为 makefile就像一个Shell脚本一样,其中也可以执行操作系统的命令。
1.0从一个简单的例子开始
这里,我们用一个示例来说明Makefile的书写规则。以便给大家一个感兴认识。这个示例来源于GNU的make使用手册,在这个示例中,在这个例子中,我们会看到一份主程序代码(main.c)、三份函数代码(getop.c、stack.c、getch.c)以及一个头文件(calc.h)。通常情况下,我们需要这样编译它:
gcc -o calc main.c getch.c getop.c stack.c
如果没有makefile,在开发+调试程序的过程中,我们就需要不断地重复输入上面这条编译命令,要不就是通过终端的历史功能不停地按上下键来寻找最近执行过的命令。这样做两个缺陷:
一旦终端历史记录被丢失,我们就不得不从头开始;
任何时候只要我们修改了其中一个文件,上述编译命令就会重新编译所有的文件,当文件足够多时这样的编译会非常耗时。
为了减少麻烦和节约时间,我们要写一个Makefile来告诉make命令如何编译和链接这几个文件。我们的规则是:
1.如果这个工程没有编译过,那么我们的所有C文件都要编译并被链接。
2.如果这个工程的某几个C文件被修改,那么我们只编译被修改的C文件,并链接目标程序。
3.如果这个工程的头文件被改变了,那么我们需要编译引用了这几个头文件的C文件,并链接目标程序。
只要我们的Makefile写得够好,所有的这一切,我们只用一个make命令就可以完成,make命令会自动智能地根据当前的文件修改的情况来确定哪些文件需要重编译,从而自己编译所需要的文件和链接目标程序。
1.1 Makefile的规则
target ... : prerequisites ...
command
...
...
target也就是一个目标文件,可以是Object File,也可以是执行文件。
prerequisites就是,要生成那个target所需要的文件或是目标。
command也就是make需要执行的命令。(任意的Shell命令)
这是一个文件的依赖关系,也就是说,target这一个或多个的目标文件依赖于prerequisites中的文件,其生成规则定义在command中。说白一点就是说,prerequisites中如果有一个以上的文件比target文件要新的话,command所定义的命令就会被执行。这就是Makefile的规则。也就是Makefile中最核心的内容。
通过了解规则,我们就掌握了makefile的核心内容,但是仅仅靠这个是无法自己编写好一个完美的makefile文件的。
这里我们看回到我们之前的示例,按照这个规则我们可以写出如下的一个简单的makefile文件:
calc: main.c getch.c getop.c stack.c
gcc -o calc main.c getch.c getop.c stack.c
我么可以看到它主要分成了三个部分,第一行冒号之前的calc,就是我们所要编译的这个程序calc。冒号后面的部分(main.c getch.c getop.c stack.c),就是我们称之为依赖关系表,也就是编译calc所需要的文件,这些文件只要有一个发生了变化,就会触发该语句的第三部分,我们称其为命令部分。
这里值得注意的是:请注意,在第二行的“gcc”命令之前必须要有一个tab缩进。语法规定Makefile中的任何命令之前都必须要有一个tab缩进,否则make就会报错。
这样,一个可以进行编译的makefile文件写出来了,但是,我们可以看出,整个makefile文件比较基础,没有进行过优化,这样,在编译比较大且文件多的工程的时候效率是远远达不到的,所以我们需要通过下面的学习来进行重构,以提高整个makefile的编译效率。
1.3makefile中使用变量
有时候我们会需要在makefile中增添文件,我们这个makefile显然可以直接在依赖链和指令中直接添加文件名来完成,但是当文件较大时想要直接添加可能就比较复杂和耗费时间了,所以,为了makefile的易维护,在makefile中我们可以使用变量。makefile的变量也就是一个字符串,理解成C语言中的宏可能会更好。
cc = gcc
prom = calc
src = main.c getch.c getop.c stack.c $(prom): $(src)
$(cc) -o $(prom) $(src)
1.4、make是如何工作的
1、make会在当前目录下找名字叫“Makefile”或“makefile”的文件。当名字不为这俩着之一时,可以用以下方法指定。
make f 文件名
2、如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到“calc”这个文件,并把这个文件作为最终的目标文件。
3、如果calc文件不存在,或是calc所依赖的后面的 .o 文件的文件修改时间要比edit这个文件新,那么,他就会执行后面所定义的命令来生成edit这个文件。
4、如果calc所依赖的.o文件也不存在,那么make会在当前文件中找目标为.o文件的依赖性,如果找到则再根据那一个规则生成.o文件。(这有点像一个堆栈的过程)
5、当然,你的C文件和H文件是存在的啦,于是make会生成 .o 文件,然后再用 .o 文件生命make的终极任务,也就是执行文件calc了。
cc = gcc
prom = calc
deps = calc.h
obj = main.o getch.o getop.o stack.o
$(prom): $(obj)
$(cc) -o $(prom) $(obj) main.o: main.c $(deps)
$(cc) -c main.c getch.o: getch.c $(deps)
$(cc) -c getch.c getop.o: getop.c $(deps)
$(cc) -c getop.c stack.o: stack.c $(deps)
$(cc) -c stack.c
这样一来,上面的问题显然是解决了,但是现在makefile文件又变的啰嗦了,这里还是可以用变量的方法来对其进行简化
cc = gcc
prom = calc
deps = calc.h
obj = main.o getch.o getop.o stack.o $(prom): $(obj)
$(cc) -o $(prom) $(obj) %.o: %.c $(deps)
$(cc) -c $< -o $@
到目前为止,我们已经有了一个不错的makefile,至少用来维护这个小型工程是没有什么问题了。
1.5清空目标文件的规则
cc = gcc
prom = calc
deps = calc.h
obj = main.o getch.o getop.o stack.o $(prom): $(obj)
$(cc) -o $(prom) $(obj) %.o: %.c $(deps)
$(cc) -c $< -o $@
.PHONY:clean
clean:
rm -rf $(obj) $(prom)
有了上面最后两行代码,当我们在终端中执行make clean命令时,它就会去删除该工程生成的所有编译文件,从而保持整个工程整洁。.PHONY意思表示clean是一个“伪目标”,。而在rm命令前面加了一个小减号的意思就是,也许某些文件出现问题,但不要管,继续做后面的事。当然,clean的规则不要放在文件的开头,不然,这就会变成make的默认目标,相信谁也不愿意这样。不成文的规矩是——“clean从来都是放在文件的最后”。
1.6使用函数使得makefile更智能
如果我们需要往工程中添加一个.c或.h,可能同时就要再手动为obj常量再添加第一个.o文件,如果这列表很长,代码会非常难看,为此,我们需要用到Makefile中的函数,这里我们演示两个:
cc = gcc
prom = calc
deps = $(shell find ./ -name "*.h")
src = $(shell find ./ -name "*.c")
obj = $(src:%.c=%.o) $(prom): $(obj)
$(cc) -o $(prom) $(obj) %.o: %.c $(deps)
$(cc) -c $< -o $@ clean:
rm -rf $(obj) $(prom)
推荐书籍下载链接
http://download.csdn.net/download/noticeable/10242106
Makefile工具下载
SCons: A software construction tool
https://www.lrde.epita.fr/~adl/autotools.html
参考资料
http://blog.csdn.net/ruglcc/article/details/7814546
https://www.gnu.org/software/make/manual/make.html
https://www.zhihu.com/question/23792247
https://yq.aliyun.com/articles/9243
http://blog.csdn.net/liang13664759/article/details/1771246
http://blog.csdn.net/haoel/article/details/2886/
https://www.jianshu.com/p/ff0e0e26c47a
6、GNU makefile工程管理学习的一个例子的更多相关文章
- ARM裸机开发之交叉工具链和MakeFile工程管理
一.交叉工具链 嵌入式Linux开发采用交叉开发,简单来说就是在宿主机(PC机)上面编译出能够在其他硬件平台上面运行的程序.在这个过程中,需要用到许多的交叉工具,这些交叉工具的集合就叫做交叉工具链.下 ...
- Makefile 工程管理
Makefile 工程管理 Makefile 规则 --变量 在Makefile中,用户除了可以自己定义变量外,还可以使用存在系统已经定义好的默认变量 $^:代表所有的依赖文件 $@:代表目标 $&l ...
- 1-3 编程基础 makefile工程管理
GNU make Linux程序员必须学会使用GNU make来构建和管理自己的软件工程.GNU的make能够使整个工程的编译.链接只需要一个命令就可以完成. makefile make在执行时,需要 ...
- linux应用程序设计--Makefile工程管理
Makefile文件描述了整个工程的编译.链接等规则.包括:工程中哪些源文件需要编译以及如何编译:需要创建哪些库文件以及如何创建这些库文件.如何产生最终的可执行文件. Makefile相关术语 1.规 ...
- 1.3 Makefile 工程管理
1. 为什么得用Makefile 单步命令生成led.bin [root@cfm880 lesson1]# cd .. [root@cfm880 Part1]# mkdir lesson3 [root ...
- Makefile工程管理器及万能模板
Linux 环境下的程序员如果不会使用GNU make来构建和管理自己的工程,应该不能算是一个合格的专业程序员,至少不能称得上是 Unix程序员.在 Linux(unix )环境下使用GNU 的m ...
- makefile工程管理
个人理解吧,makefile就是写一个指定格式的文件,将一系列的编译.链接.转换等操作打包在一起,方便以后一键生成可执行的二进制文件而产生的.下面记录一下这种文件的写法,方便以后忘了来查询. make ...
- [国嵌笔记][017][Makefile工程管理]
Makefile的用途 1.make能够使整个程序的编译.链接只需一个命令就可以完成 2.make的工作主要依赖于Makefile的文件.Makefile文件描述了整个程序的编译.链接等规则,使之自动 ...
- 学习总结:工程管理与makefile
工程管理与makefile 一.为什么需要makefile和make 一个工程中的源文件可能很多,按照类型.功能.模块分别放在若干个目录中,为了有效地管理软件工程,更高效地编译整个工程,需要用到mak ...
随机推荐
- (转)Java程序员简历模板
本简历模板由国内首家互联网人才拍卖网站「 JobDeer.com 」提供. (括号里的是我们的顾问编写的说明,建议在简历书写完成后统一删除) 先讲讲怎样才是一份好的技术简历 首先,一份好的简历不光说明 ...
- (转)JavaScript的压缩
JavaScript的压缩 (转自)http://blog.csdn.net/ybygjy/article/details/6995435 简述 如果非常着急,这块可以跳过直接从约束条件开始也行. J ...
- JVM 体系结构概述 (一)
一.jvm运行在操作系统之上的,它与硬件没有直接交互: 二.JVM体系结构概览 JVM的基本结构:类加载器.执行引擎.运行时数据区.本地方法接口: 过程:class文件 ----> 类加载器 - ...
- MySql5.5 SQL优化 慢查询日志存储
一.MySql的慢查询日志的开启和存储 1.查看是否把没有使用索引的SQL记录到慢查询日志中,查看 log_queries_not_using_indexes 变量; show VARIABLES L ...
- linux利用crontab设置定时任务运行jar包
参考链接: 1.http://blog.csdn.net/javadhh/article/details/42779505 2.http://blog.csdn.net/cctv_liu/articl ...
- SystemTap 工作原理
<systemtap原理及使用> https://www.cnblogs.com/youngerchina/p/5624588.html 这篇帖子前边系统介绍了systemtap的工作原理 ...
- 20180613更新 leetcode刷题
最近就是忙工作项目 工作间隙就刷了刷LEETCODE 所以没啥更新 // 1111111.cpp: 定义控制台应用程序的入口点. // #include "stdafx.h" #i ...
- 动态代理jdk和cglib的区别
学习来源贴:http://www.cnblogs.com/jqyp/archive/2010/08/20/1805041.html JDK实现动态代理需要实现类通过接口定义业务方法,对于没有接口的类, ...
- servlet 知识点 WebServlet("/")与WebServlet("/*")的区别
(1)一个servlet的请求路径,如果配置成"/",那么它可以拦截除了.jsp之外的所有的请求. (2)一个servlet的请求路径,如果配置成"/*",那么 ...
- 使用delphi 10.2 开发linux 上的webservice
前几天做了linux下apache的开发,今天做一个linux 下的webservice ,以供客户端调用. 闲话少说,直接干. 新建一个工程.选other...,选择如图. 继续输入服务名 然后就生 ...