Make 学习笔记(1)
Make 学习笔记(1)
参考:
GNU make 学习总结(1)
基础
make是帮助程序员使编译器明白如何编译工程的一种工具; 核心是规则.
规则一般由三部分组成:
- 目标(target)
- 必要条件(prerequisite)
- 命令(command)
具体的书写规则一般为:
target1 target2 : prereq1 prereq2
command1
command2
目标和条件之间由冒号隔开, 命令在目标和条件的下一行, 并以Tab开头.每条规则中可以有多个目标,多个条件和多个命令.
make的原理简单剖析:
当规则中规定的目标文件不存在或者必要条件中某个文件的时间戳比目标文件的时间戳要新,就执行下面的命令,生成新的目标文件.
make会将第一条规则中的目标作为最终目标.
测试的例子:
|-- InputSpeed
|-- main.c
|-- timeutil.c
|-- timeutil.h
|-- wdshow.c
|-- wdshow.h
如上, 目录文件如此.
原始的Makefile文件如下:
main : main.o timeutil.o wdshow.o
gcc timeutil.o wdshow.o main.o -o main
main.o : main.c wdshow.c
gcc -c main.c
wdshow.o : wdshow.c wdshow.h timeutil.h
gcc -c wdshow.c
timeutil.o : timeutil.c timeutil.h
gcc -c timeutil.c
Make会将第一条规则中的目标作为最终目标,也就是我们的可执行文件main,在生成main的时候,需要三个对象文件作为必要条件文件,如果对象文件不存在或不是最新,Make会继续去找是否有规则以该文件为目标文件,找到的话就执行对应的命令,否则就会报错。这个过程大致如下,是一个递归的算法
make(rule) {
for target in rule
for each prerequisite file in rule
if file exist and is up-to-date
return "ok";
else if there is a rule(called rule-file) for file
make(rule-file);
else
return "error"
for each command in rule
run command
}
执行make命令,输出如下。可以看到,第一条命令是最后执行的,这不难理解,因为Make查找的过程是一个递归过程,最先入栈的程序将在最后被执行。
规则
基础知识
变量及自动变量
普通变量的定义是$
后面跟字母或者是括号字母,如$x, $(x)
. 但是在make系统中定义了部分自动变量:
$@ 目标文件名
$% 档案文件成员,是指以a.o(b.o)这种形式作为目标时,括号中的内容
$< 第一个必要条件文件名
$? 时间戳在目标文件之后的所有必要条件文件名,空格隔开
$^ 所有必要条件的文件名,空格隔开,这份列表删除了重复的文件名
$+ 和$^一样,只是未删除重复的文件名
$* 目标的主文件名(即不包括后缀)
以上变量都有两个变体, 加D表示文件的目录部分, 加F表示文件的文件名部分, 注意要加括号, 如$(@D), $(@F)
等.
可以简化makefile如下:
main : main.o timeutil.o wdshow.o
gcc $^ -o $@
main.o : main.c wdshow.c
gcc -c $<
wdshow.o : wdshow.c wdshow.h timeutil.h
gcc -c $<
timeutil.o : timeutil.c timeutil.h
gcc -c $<
这里需要解释下, 每一条规则是并行的, 但这里的自动变量是针对一条规则中说的, 在规则间没有这种自动变量.
假想目标
不指向任何实际文件的目标, 所以总是被更新, 对应的命令总会被执行, 对应的必要条件以及依赖总保持更新.
注意 : 如果clean是一个假想目标,那么当工程中也有一个命名为clean的文件时, 由于make并不知道clean到底是个实际文件还是假想目标(事实上make会优先确定clean是一个实际文件)同时clean也没有必要条件和依赖的更新, make clean
会永远返回文件是最新的. 要确定向编译器表明某一目标是假想目标需要使用关键字.PHONY
, 例如.PHONY : clean all
需要将clean
指定成.PHONY
的一个必要条件,这样clean就总会被更新了,这里.PHONY
是一个特殊目标,它告诉Make它的必要条件都是假想目标。于是我们可以在原来的Makefile中加入几行, 变为:
main : main.o timeutil.o wdshow.o
gcc $^ -o $@
main.o : main.c wdshow.c
gcc -c $<
wdshow.o : wdshow.c wdshow.h timeutil.h
gcc -c $<
timeutil.o : timeutil.c timeutil.h
gcc -c $<
.PHONY: clean all
all: main
clean:
rm main *.o
几个比较常用的假想目标:
clean 清除编译得到的二进制文件
all 所有需要生成的可执行文件
install 经过make all步骤后,在系统中安装生成的二进制程序
distclean 比clean更彻底的删除,包括由configure生成的Makefile文件
TAGS 提供可供编辑的标记表
info 从Textinfo源码创建GNU info源码
check 执行相关测试
类似.PHONY
的特殊目标还有以下几个:
.SUFFIXES 指定Makefile已知后缀列表,用于后缀规则
.INTERMEDIATE 必要条件视为中间文件,make过程如果生成了指定的中间文件,完成后会被删除
.SECONDARY 同样指定中间文件,但make完成后不会被删除
.PRECIOUS make运行中断时不删除指定的目标文件
.DELETE_ON_ERROR make运行中断时删除指定的目标文件
VPATH 和 vpath
改善工程文件目录, 如将之前工程的形式改为:
|-- InputSpeed
\-- include
|-- wdshow.h
|-- timeutil.h
\-- src
|-- timeutil.c
|-- wdshow.c
|-- main.c
Makefile
如果不改变Makefile不能正确找到.c
和.h
文件, 可以使用 VPATH = src include
方式来找到文件, 但是VPATH只返回找到的第一个文件, 并且可能存在重名文件. 更好的方式是使用vpath pattern directory
的形式来指定在哪个文件夹下搜索哪个文件, 并且对于头文件应该给GCC加上参数.具体的makefile如下:
vpath %.c src
vpath %.h include
CFLAGS = -I include
main : main.o timeutil.o wdshow.o
gcc $^ -o $@
main.o : main.c wdshow.c
gcc $(CFLAGS) -c $< -o $@
wdshow.o : wdshow.c wdshow.h timeutil.h
gcc $(CFLAGS) -c $< -o $@
timeutil.o : timeutil.c timeutil.h
gcc $(CFLAGS) -c $< -o $@
.PHONY: clean all
all: main
clean:
rm main *.o
编译规则的分类:
== 具体规则 ==
即目标, 条件, 命令都明确给出的规则.
== 模式规则 ==
依据是x.o
和x.c
之间具有对应关系, 需要编译出x.o
就会对应去寻找x.c
.
注意:
在一个规则中,如果主文件名中包含了%就表示这是一个模式规则。所谓模式规则,是指对符合这个模式的目标都采用这个规则,注意%和通配符的不同,在Makefile中是可以使用通配符的,*.c表示的是所有以c结尾的文件的集合,而%.c表示所有以c结尾的文件都匹配这条规则,一定要注意区分。
所有的内置规则都是模式规则,使用make -p可以看到这些内置规则,用其中生成%.o的这一条作为例子:
CC = gcc
CFLAGS = -I include
COMPILE.c = $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c
OUTOUT_OPTION = -o $@
%.o: %.c
# commands to execute (built-in):
$(COMPILE.c) $(OUTPUT_OPTION) $<
== 静态模式规则 ==
与模式规则基本一样,只是规定了模式的范围,格式如下,模式目标文件必须在指定的变量中
$(OBJECTS): %.o: %c
== 隐含规则 ==
就是make自带的内置规则, 其实就是模式规则. 比如默认的x.o
由x.c
生成.当没有设定规则时, make就会使用内置规则. 简化上面的规则如:
vpath %.c src
vpath %.h include
CFLAGS = -I include
main: main.o timeutil.o wdshow.o
main.o: wdshow.h
wdshow.o: wdshow.h timeutil.h
timeutil.o: timeutil.h
== 自动生成依赖 ==
gcc有一个神奇的功能-MM
, 可以得到头文件的依赖项, 如在我的电脑上以刚才的工程结构, 输入:
gcc -I include -MM src/main.c src/timeutil.c src/wdshow.c
可以得到:
main.o: src/main.c include/wdshow.h
timeutil.o: src/timeutil.c include/timeutil.h
wdshow.o: src/wdshow.c include/wdshow.h include/timeutil.h
Make 学习笔记(1)的更多相关文章
- js学习笔记:webpack基础入门(一)
之前听说过webpack,今天想正式的接触一下,先跟着webpack的官方用户指南走: 在这里有: 如何安装webpack 如何使用webpack 如何使用loader 如何使用webpack的开发者 ...
- PHP-自定义模板-学习笔记
1. 开始 这几天,看了李炎恢老师的<PHP第二季度视频>中的“章节7:创建TPL自定义模板”,做一个学习笔记,通过绘制架构图.UML类图和思维导图,来对加深理解. 2. 整体架构图 ...
- PHP-会员登录与注册例子解析-学习笔记
1.开始 最近开始学习李炎恢老师的<PHP第二季度视频>中的“章节5:使用OOP注册会员”,做一个学习笔记,通过绘制基本页面流程和UML类图,来对加深理解. 2.基本页面流程 3.通过UM ...
- 2014年暑假c#学习笔记目录
2014年暑假c#学习笔记 一.C#编程基础 1. c#编程基础之枚举 2. c#编程基础之函数可变参数 3. c#编程基础之字符串基础 4. c#编程基础之字符串函数 5.c#编程基础之ref.ou ...
- JAVA GUI编程学习笔记目录
2014年暑假JAVA GUI编程学习笔记目录 1.JAVA之GUI编程概述 2.JAVA之GUI编程布局 3.JAVA之GUI编程Frame窗口 4.JAVA之GUI编程事件监听机制 5.JAVA之 ...
- seaJs学习笔记2 – seaJs组建库的使用
原文地址:seaJs学习笔记2 – seaJs组建库的使用 我觉得学习新东西并不是会使用它就够了的,会使用仅仅代表你看懂了,理解了,二不代表你深入了,彻悟了它的精髓. 所以不断的学习将是源源不断. 最 ...
- CSS学习笔记
CSS学习笔记 2016年12月15日整理 CSS基础 Chapter1 在console输入escape("宋体") ENTER 就会出现unicode编码 显示"%u ...
- HTML学习笔记
HTML学习笔记 2016年12月15日整理 Chapter1 URL(scheme://host.domain:port/path/filename) scheme: 定义因特网服务的类型,常见的为 ...
- DirectX Graphics Infrastructure(DXGI):最佳范例 学习笔记
今天要学习的这篇文章写的算是比较早的了,大概在DX11时代就写好了,当时龙书11版看得很潦草,并没有注意这篇文章,现在看12,觉得是跳不过去的一篇文章,地址如下: https://msdn.micro ...
- ucos实时操作系统学习笔记——任务间通信(消息)
ucos另一种任务间通信的机制是消息(mbox),个人感觉是它是queue中只有一个信息的特殊情况,从代码中可以很清楚的看到,因为之前有关于queue的学习笔记,所以一并讲一下mbox.为什么有了qu ...
随机推荐
- A - Set of Strings
Time Limit:1000MS Memory Limit:262144KB 64bit IO Format:%I64d & %I64u Description You ar ...
- mysql server安装(windows)
1 在 https://dev.mysql.com/downloads/mysql/ 上下载mysql压缩包 2 解压,并把bin目录加入环境变量 3 初始化,完成后会在mysql根目录下生成data ...
- 洛谷 - P3966 - 单词 - AC自动机
https://www.luogu.org/problemnew/show/P3966 因为文本串就是字典本身,所以这个和平时的AC自动机不太一样.平时的query要沿着fail树把子树的出现次数依次 ...
- MYSQL5.7版本解决sql_mode=only_full_group_by问题
在安装有些二开框架时会遇到下面的问题,在填写完数据库密码之后他会提示你请在mysql配置文件中修改ql-mode去掉ONLY_FULL_GROUP_BY,但是我们去mysql的配置文件中查找此配置,有 ...
- JavaScript代码风格和分号使用问题
1.推荐代码风格 JavaScript Standard Style 规定相对松散更多人使用此规范 Airbnb JavaScript Style 规定更严格但也没毛病 2.JavaScript代 ...
- 单片机C基本编程规范
为了提高源程序的质量和可维护性,从而最终提高软件产品生产力,特编写此规范.本标准规定了程序设计人员进行程序设计时必须遵循的规范.本规范主要针对单片机编程语言和08编译器而言,包括排版.注释.命名.变量 ...
- 105 Construct Binary Tree from Preorder and Inorder Traversal 从前序与中序遍历序列构造二叉树
给定一棵树的前序遍历与中序遍历,依据此构造二叉树.注意:你可以假设树中没有重复的元素.例如,给出前序遍历 = [3,9,20,15,7]中序遍历 = [9,3,15,20,7]返回如下的二叉树: ...
- freertos之任务
taskYIELD(): 通知调度器自己放弃运行态,可立即进行任务切换,而不必等到当前任务的时间片耗尽.这对于相同任务优先级的2个任务来说可加速效率.
- Unity Shader入门精要学习笔记 - 第6章 开始 Unity 中的基础光照
转自冯乐乐的<Unity Shader入门精要> 通常来讲,我们要模拟真实的光照环境来生成一张图像,需要考虑3种物理现象. 首先,光线从光源中被发射出来. 然后,光线和场景中的一些物体相交 ...
- MVC系列学习(十六)-区域的学习
1.查找控制器的过程 1.1调用其他项目中的控制器 a.先到网站根目录下的bin文件夹下,遍历所有的程序集 b.找到以Controller结尾的类 c.再找出其中继承了Controller的类 d.接 ...