专注于互联网--专注于架构
最新标签
网站地图
文章索引
Rss订阅
首页
CrazyCoder
原创
翻译
数据库
项目管理
QQ协议开发
开发语言
Web开发
验证码识别
技术综合
服务器
Dig版
标签
Rss订阅
首页
»
嵌入式开发
» gnumake:善用GNU Make做开发 »正文
gnumake:善用GNU Make做开发
来源: 发布时间:星期四, 2008年12月11日 浏览:8次 评论:0
=tf width="98%" align=center border=0>
=bw width="100%">
=htd id=font_word style="FONT-SIZE: 14px; FONT-FAMILY: 宋体, Verdana, Arial, Helvetica, sans-ser
">作者:肖文鹏
dCYfbaiducukf6o在编写小型
Linux应用
时
般情况下只会有少数几个源文件
这样
员能够很容易地理清它们之间
包含和引用关系
但随着软件项目逐渐变大
对源文件
处理也将变得越来越复杂起来
此时单纯依赖手工方式进行管理
做法就显得有些力不从心了
为此
Linux专门为软件开发提供了
个自动化管理工具 GNU make
通过它
员可以很方便地管理软件编译
内容、方式和时机
从而使
员能够把主要精力集中在代码
编写上
dCYfbaiducukf6o
dCYfbaiducukf6o & nbspmake将整个软件项目
代码分开放在几个小
源文件里
在改动其中
个文件
时候
可以只对该文件重新进行编译
然后重新连接所有
目标文件
对于那些由许多源文件组成
大型软件项目来说
全部重新进行编译需要花费很长
时间
而采用这种项目管理方法则可以极大地提高工作效率
让原本复杂繁琐
开发工作变简单
dCYfbaiducukf6o
dCYfbaiducukf6o Makefile文件
dCYfbaiducukf6o
dCYfbaiducukf6o GNU make 是
个用来控制软件构建过程
自动工具
员通过定义构建规则来控制代码
创建过程
这些规则通常定义在
个名为Makefile
文件中
Makefile被用来告诉make编译哪些文件、怎样编译和何时编译
Makefile中
每条规则事实上都包含如下
些内容:
dCYfbaiducukf6o ◆ 目标(target)是make最终需要创建
对象;
dCYfbaiducukf6o ◆ 依赖(dependency)通常是
个列表
指明编译目标时需要用到
其它文件;
dCYfbaiducukf6o ◆ 命令(command)也是
个列表
指明从依赖文件创建出目标对象所需要执行
命令
dCYfbaiducukf6o
dCYfbaiducukf6o 虽然Makefile中
目标通常都是可执行
但事实上可以是诸如文本文件和HTML页面等任何内容
甚至能够用来测试或设置环境变量
Makefile中
命令则不仅可以是编译命令
还可以是任何Shell命令
dCYfbaiducukf6o
dCYfbaiducukf6o 先来看
个例子
假设整个软件项目是由control.c、io.c和
.c三个源文件所构成
编写
Makefile文件内容如下:
dCYfbaiducukf6o
dCYfbaiducukf6oall : program
dCYfbaiducukf6oprogram : control.o ui.o
.o
dCYfbaiducukf6ogcc -o program control.o ui.o
.o
dCYfbaiducukf6ocontrol.o : control.c
dCYfbaiducukf6ogcc -Wall -c -o control.o control.c
dCYfbaiducukf6oui.o : ui.c
dCYfbaiducukf6ogcc -Wall -c -o ui.o ui.c
dCYfbaiducukf6o
.o :
.c
dCYfbaiducukf6ogcc -Wall -c -o
.o
.c
dCYfbaiducukf6oclean :
dCYfbaiducukf6orm -f program *.o
dCYfbaiducukf6o
dCYfbaiducukf6o
dCYfbaiducukf6o
dCYfbaiducukf6o 在将上述Makefile文件与源文件保存到同
目录之后
就可以在命令行中输入“make”命令来编译整个项目了
make在执行过程中
首先会查找到 Makefile文件第
条规则中
目标
即上述文件中
all
根据设定好
规则
该目标需要依赖于program
由于all并不是
个已经存在
文件
所以每次在make被
时候
显然都需要先检查program
继续往下不难发现
program目标是依赖于control.o、ui.o和
.o
这就意味着如果其中任何
个比生成
可执行文件要新
那么就需要重新构建可执行文件program
否则就没有必要执行这
步了
dCYfbaiducukf6o
dCYfbaiducukf6o 在Makefile文件
其余部分
为每
个中间生成
目标文件都专门定义了
条规则
用来指明创建过程中它们与C源文件
依赖性
也就是说
如果
个特定
C源文件被更新了
那么与之对应
目标文件也必须重新生成
下面是make在构建项目过程中
输出结果:
dCYfbaiducukf6o
dCYfbaiducukf6o#make
dCYfbaiducukf6ogcc -Wall -c -o control.o control.c
dCYfbaiducukf6ogcc -Wall -c -o ui.o ui.c
dCYfbaiducukf6ogcc -Wall -c -o
.o
.c
dCYfbaiducukf6ogcc -o program control.o ui.o
.o
dCYfbaiducukf6o
dCYfbaiducukf6o
dCYfbaiducukf6o
dCYfbaiducukf6o 不难看出
首先是C源文件被编译成目标文件
然后才是目标文件被连接成最终
可执行文件
由于相互间依赖关系
制约
这些步骤会被有条不紊地依次执行
最终可执行文件要求目标文件都被更新过
而每个目标文件则要求C源文件被更新过
如果此时重新执行“make”命令
会出现下面
结果
原因是
已经被编译过了
并且没有做过任何改动
所以就没有再编译
必要了:
dCYfbaiducukf6o
dCYfbaiducukf6o# make
dCYfbaiducukf6omake: Nothing to be done for 'all'.
dCYfbaiducukf6o
dCYfbaiducukf6o
dCYfbaiducukf6o
dCYfbaiducukf6o 如果只是改变了其中
部分文件
那么make会自动检测出需要对哪些源文件重新进行编译
并连接成最后
可执行文件
用户可以参考下面
过程:
dCYfbaiducukf6o
dCYfbaiducukf6o#touch
.c
dCYfbaiducukf6o# make
dCYfbaiducukf6ogcc -Wall -c -o
.o
.c
dCYfbaiducukf6ogcc -o program control.o ui.o
.o
dCYfbaiducukf6o
dCYfbaiducukf6o
dCYfbaiducukf6o
dCYfbaiducukf6o 当make检测到
.o目标时
发现
.c文件已经被更新
于是
.o文件必须被重新编译
相应地program需要被重新连接
make
魅力就在于能够自动进行条件检测
并采取适当
行动
它永远也不会去编译那些没有改动过
源文件
因此大大节省了在开发大型软件项目时所浪费在编译上
时间
dCYfbaiducukf6o
dCYfbaiducukf6o 变量
dCYfbaiducukf6o
dCYfbaiducukf6o 为了简化Makefile
编写
make引入了变量
变量实际上是为文本串在 Makefile中定义
个便于记忆
名称
变量
定义和应用与Linux
环境变量
样
变量名大写
变量
旦定义之后
就可以通过将变量名用圆括号包起来
并在前面加上“$”符号来进行引用
dCYfbaiducukf6o
dCYfbaiducukf6o 变量
般都在Makefile
头部定义
如果变量
值发生了改变
很显然只需在
个地方进行修改就可以了
从而大大简化了Makefile
维护
下面是将前面用到
Makefile利用变量进行改写后
结果:
dCYfbaiducukf6o
dCYfbaiducukf6oOBJS = control.o ui.o
.o
dCYfbaiducukf6oCC = GCC
dCYfbaiducukf6oCFLAGS = -Wall
dCYfbaiducukf6oall : program
dCYfbaiducukf6oprogram : $(OBJS)
dCYfbaiducukf6o$(CC) $(OBJS) -o program
dCYfbaiducukf6ocontrol.o : control.c
dCYfbaiducukf6o$(CC) $(CFLAGS) -c -o control.o control.c
dCYfbaiducukf6oui.o : ui.c
dCYfbaiducukf6o$(CC) $(CFLAGS) -c -o ui.o ui.c
dCYfbaiducukf6o
.o :
.c
dCYfbaiducukf6o$(CC) $(CFLAGS) -c -o
.o
.c
dCYfbaiducukf6oclean :
dCYfbaiducukf6o rm -f program $(OBJS)
dCYfbaiducukf6o
dCYfbaiducukf6o
dCYfbaiducukf6o
dCYfbaiducukf6o & nbspmake将其使用
变量细分为两类:递归展开变量和简单展开变量
递归展开变量在被引用时会逐层展开
即如果在展开式中包含了对其它变量
引用
则这些变量也会被展开
直到没有需要被展开
变量为止
假设变量TOPDIR和SUBDIR
定义如下:
dCYfbaiducukf6o
dCYfbaiducukf6oTOPDIR = /home/xiaowp
dCYfbaiducukf6oSUBDIR = $(TOPDIR)/project
dCYfbaiducukf6o
dCYfbaiducukf6o
dCYfbaiducukf6o
dCYfbaiducukf6o 此时变量SUBDIR
值在解析时会被正确地展开为/home/xiaowp/project
但对于下面
定义:
dCYfbaiducukf6o
dCYfbaiducukf6oTOPDIR = /home/xiaowp
dCYfbaiducukf6oSUBDIR = $(TOPDIR)/project
dCYfbaiducukf6oSUBDIR = $(SUBDIR)/src
dCYfbaiducukf6o
dCYfbaiducukf6o
dCYfbaiducukf6o
dCYfbaiducukf6o 很清楚
希望得到
结果是/home/xiaowp/project/src
但实际并非如此
SUBDIR在引用时会被递归展开
从而陷入
个无限循环当中
make能够检测到这个问题并报告如下
:
dCYfbaiducukf6o *** Recursive variable 'SUBDIR' references itself (eventually). Stop
dCYfbaiducukf6o
dCYfbaiducukf6o 为了避免这个问题
可以使用简单展开变量
与递归展开变量在引用时展开不同
简单展开变量是在定义处展开
并且只展开
次
从而消除了变量
嵌套引用
在定义时
其语法与递归展开变量有细微
不同:
dCYfbaiducukf6o
dCYfbaiducukf6oTOPDIR = /home/xiaowp
dCYfbaiducukf6oSUBDIR := $(TOPDIR)/project
dCYfbaiducukf6oSUBDIR
/src
dCYfbaiducukf6o
dCYfbaiducukf6o
dCYfbaiducukf6o
dCYfbaiducukf6o & nbspSUBDIR在第
次定义时使用“:=”将其值设置为“/home/xiaowp/project”
而在第二次定义时则使用“
”在已有
基础上添加“/src”
这样就使得SUBDIR
最终值变为“/home/xiaowp/project/src”
许多
员在Makefile中只使用简单展开变量
以避免可能出现
dCYfbaiducukf6o
dCYfbaiducukf6o 除了用户自定义变量之外
在Makefile中还可以使用环境变量、自动变量和预定义变量
使用环境变量
方法相对来讲比较简单
make在启动时会自动读取系统当前已经定义了
环境变量
并且会创建与之具有相同名称和数值
变量
需要注意
是
如果用户在Makefile中定义了相同名称
变量
那么用户自定义变量将会覆盖同名
环境变量
dCYfbaiducukf6o 此外
make还提供了
些预定义变量和自动变量
但它们看起来都不如自定义变量那么直观
之所以称为自动变量是
make会自动用特定
、熟知
值来替换它们
表1给出了常用
部分自动变量
dCYfbaiducukf6o
dCYfbaiducukf6o 利用make
自动变量和预定义变量
可以简化前面给出
那个Makefile文件:
dCYfbaiducukf6o
dCYfbaiducukf6oOBJS = control.o ui.o
.o
dCYfbaiducukf6oCC = GCC
dCYfbaiducukf6oCFLAGS = -Wall
dCYfbaiducukf6oall : program
dCYfbaiducukf6oprogram : $(OBJS)
dCYfbaiducukf6o$(CC) $(OBJS) -o $@
dCYfbaiducukf6ocontrol.o : control.c
dCYfbaiducukf6o$(CC) $(CFLAGS) -c -o $@ $<
dCYfbaiducukf6oui.o : ui.c
dCYfbaiducukf6o$(CC) $(CFLAGS) -c -o $@ $<
dCYfbaiducukf6o
.o :
.c
dCYfbaiducukf6o$(CC) $(CFLAGS) -c -o $@ $<
dCYfbaiducukf6oclean :
dCYfbaiducukf6o $(RM) program $(OBJS)
dCYfbaiducukf6o
dCYfbaiducukf6o
dCYfbaiducukf6o
dCYfbaiducukf6o 伪目标
dCYfbaiducukf6o
dCYfbaiducukf6o 在Makefile中
并不是所有
目标都对应于磁盘上
文件
有
目标存在只是为了形成
条规则
从而完成特定
工作
并不生成新
目标文件
这样
目标称为伪目标
它并不是真正意义上
目标文件
只是为了满足Makefile
语法规则而存在
dCYfbaiducukf6o
dCYfbaiducukf6o 在已经给出
Makefile文件中
最后
个目标clean就是伪目标
它规定了make应该执行
命令
当make处理到目标clean时
会先查看其对应
依赖对象
由于clean没有任何依赖对象
所以make会认为该目标是最新
而不会执行任何操作
为了编译这个目标体
必须手工执行如下命令: # make clean
dCYfbaiducukf6o
dCYfbaiducukf6o 作为惯例
clean目标
般用于删除最终生成
可执行文件和在编译过程中产生
所有目标文件
问题是
如果恰巧有
个名为clean
文件存在时该怎么办呢?此时
在这个规则里没有任何依赖对象
所以目标文件肯定是最新
规则中
命令无论如何也不会被执行
即使用命令“make clean”也无济于事
解决这
问题
方法是标明该规则中
目标是伪目标
并不对应于任何文件
这可以通过.PHONY目标实现
它告诉make不检查规则
目标文件是否存在于磁盘上
也不查找任何隐含规则
而直接假设指定
目标需要被更新就行了
在使用了.PHONY之后
前面
给出
Makefile文件就将变为如下
内容:
dCYfbaiducukf6o
dCYfbaiducukf6oOBJS = control.o ui.o
.o
dCYfbaiducukf6oCC = GCC
dCYfbaiducukf6oCFLAGS = -Wall
dCYfbaiducukf6oall : program
dCYfbaiducukf6oprogram : $(OBJS)
dCYfbaiducukf6o$(CC) $(OBJS) -o $@
dCYfbaiducukf6ocontrol.o : control.c
dCYfbaiducukf6o$(CC) $(CFLAGS) -c -o $@ $<
dCYfbaiducukf6oui.o : ui.c
dCYfbaiducukf6o$(CC) $(CFLAGS) -c -o $@ $<
dCYfbaiducukf6o
.o :
.c
dCYfbaiducukf6o$(CC) $(CFLAGS) -c -o $@ $<
dCYfbaiducukf6o.PHONY : clean
dCYfbaiducukf6oclean :
dCYfbaiducukf6o $(RM) program $(OBJS)
dCYfbaiducukf6o
dCYfbaiducukf6o
dCYfbaiducukf6o
dCYfbaiducukf6o 其它规则
dCYfbaiducukf6o
dCYfbaiducukf6o 除了可以在Makefile中明确指定规则(显示规则)之外
make还维护了
整套隐式规则
隐式规则可以在用户没有完整地给出某些命令
时候
自动执行恰当
操作
隐式规则最大
好处是可以简化Makefile
编写和维护
例如前面给出
Makefile运用隐式规则后可以简化为如下内容:
dCYfbaiducukf6o
dCYfbaiducukf6oOBJS = control.o ui.o
.o
dCYfbaiducukf6oprogram : $(OBJS)
dCYfbaiducukf6o$(CC) $(OBJS) -o $@
dCYfbaiducukf6o.PHONY : clean
dCYfbaiducukf6oclean :
dCYfbaiducukf6o $(RM) program $(OBJS)
dCYfbaiducukf6o
dCYfbaiducukf6o
dCYfbaiducukf6o
dCYfbaiducukf6o 默认目标program依赖于control.o、ui.o和
.o三个目标文件
但Makefile中并没有给出怎样编译生成这些目标
规则
此时make就会使用隐式规则
对每
个名为foo.o
目标文件
找到与之对应
源代码foo.c
然后使用“gcc -c& nbspfoo.c -o foo.o”命令来生成对应
目标文件
dCYfbaiducukf6o
dCYfbaiducukf6o 除了系统预定义
隐式规则外
在Makefile中还可以定义自己
隐式规则
这种规则也被称为模式规则
模式规则类似于普通规则
但它
目标必须含有“%”这
通配符
以便能与任何非空
相匹配
与目标对应
依赖文件中也必须使用通配符
例如下面
规则:
dCYfbaiducukf6o
dCYfbaiducukf6o%.o : %.c
dCYfbaiducukf6o $(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@
dCYfbaiducukf6o
dCYfbaiducukf6o
dCYfbaiducukf6o
dCYfbaiducukf6o 上面
规则将告诉make所有形为foo.o
目标文件
都应该根据指定
命令从源文件foo.c编译而来
dCYfbaiducukf6o
dCYfbaiducukf6o 小结
dCYfbaiducukf6o
dCYfbaiducukf6o 在构建大型
软件项目时
make是
个优秀
持续集成工具
它对于软件开发过程来讲非常重要
本文介绍了基本
make命令
以及如何编写简单实用
Makefile文件
相信用户已经能够使用make来管理软件项目
创建和维护过程了
标签:
nbsp是什么意思
gnumake项目管理
gnumake
下载文章的 PDF文档电子版 离线看
0
顶一下
相关文章
2008-12-11 23:21:54
nbsp是什么意思:BSP 概念解析
2008-12-11 22:57:09
gnumake: GNU make 指南 (zt)
2008-12-10 14:20:40
三个戴表:经验交流 给项目“戴表”
2008-12-4 16:32:07
htmlnbsp:汉化 SuSe8.0 的过程
2008-9-26 1:05:45
网页布局技巧:Web标准设计技巧之二 布局漫谈
2008-9-26 1:06:45
css选择符:CSS selector 选择符有哪些 如何合理运用?
2008-9-26 1:07:05
css图片比例:利用CSS同比例缩小图片 并且符合Web标准
2008-9-26 1:06:15
nbsp什么意思:如何设计字体的大小 以提高访客的用户体验?
2008-9-26 1:06:25
样式风格css:如何实现多风格选择 样式实时切换?
2008-9-26 1:06:54
最小离地间隙:两个层之间的为什么会有间隙(IE 3px bug)?
读者评论
共0条 分0页
发表评论
昵称:
内容:
更多
热门标签
归档日志文件
(1)
植物对环境的适应
(1)
教师要适应环境
(1)
生物对环境的适应
(1)
适应环境
(1)
数据库解决的问题
(1)
18483
(1)
错误18483
(1)
数据库优缺点
(1)
数据库的优缺点
(1)
sql嵌套
(1)
多线程实例
(1)
多线程与进程
(1)
系统诊断命令
(1)
系统诊断工具
(1)
系统诊断软件
(1)
系统诊断
(1)
rowid类型
(1)
rowid的作用
(1)
服务器被黑客控制
(1)
服务器黑客
(1)
mdb数据库安全
(1)
得到sa弱口令
(1)
sa弱口令扫描
(1)
sa弱口令扫描器
(1)
考研经验谈
(1)
sa默认密码
(1)
分页存储管理
(1)
存储过程注入
(1)
cmdshell
(1)
web数据库应用
(1)
web数据库查询
(1)
为什么要重装系统
(1)
数据库系统的安全
(1)
数据库系统安全
(1)
数据库系统
(1)
防范措施
(1)
雷达数据格式转换
(1)
数据库格式转换
(1)
数据格式转换
(1)
数据库的安全问题
(1)
数据库安全问题
(1)
sql数据集
(1)
删除词库
(1)
like用法
(1)
like的用法
(1)
access用法
(1)
默认监听端口
(1)
培训体系的概念
(1)
价值体系的概念
(1)
RSS订阅
更多
精华推荐
关于这次疯狂代码生成器的改版,以及一些抓
原创,使用轮廓以及扇形扫描实现qq的验证
疯狂代码,大型网站架构系列之三,多对多关
疯狂代码,大型网站架构系列之二,底层架构
疯狂代码,大型网站架构系列之一,前言,不
c# 一个有用的汉字转拼音类
原创发布一个C#.Net对XML文件的操
CrazyCoder原创发布一个c#获取
c#常用的正则表达式,其实对所有语言都通
ajax实现多线程,基于JAVASCRI
RSS订阅
更多
Dig排行
疯狂代码生成器简介
sql触发器:如何在SQL Server
sql语句大全:常用 SQL 语句大全
C#多线程教程系列之一,多线程入门,实例
Flash文字特效
疯狂代码,大型网站架构系列之一,前言,不
疯狂代码,大型网站架构系列之二,底层架构
会员登陆代码 一个最简单的会员登陆代码
ftp映射:FTP映射让资源应用更灵活
ajax应用实例 用AJAX编写用户注册
RSS订阅
更多
阅读排行
会员登陆代码 一个最简单的会员登陆代码
疯狂代码生成器简介
u盘文件夹被隐藏:手动清除仿文件夹图标U
sql语句大全:常用 SQL 语句大全
C#多线程教程系列之一,多线程入门,实例
疯狂代码,大型网站架构系列之一,前言,不
C#多线程教程系列之二,多线程入门,实例
c#常用的正则表达式,其实对所有语言都通
疯狂代码,大型网站架构系列之二,底层架构
疯狂代码,大型网站架构系列之三,多对多关
RSS订阅
更多
评论排行
多线程编程:UNIXLinux下的多线程
七款嵌入式Linux操作系统简介
gnumake:善用GNU M
物理内存:Linux内存管理分析之物理内
linux驱动开发:Linux驱动的开发
jrtplib:linux下基于jrtp
嵌入式linux系统:LINUX在嵌入式
linux内核完全注释:完全用GUN/L
linuxinit:Linux运行级in
linux编程:Linux 声