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.ox.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.ox.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)的更多相关文章

  1. js学习笔记:webpack基础入门(一)

    之前听说过webpack,今天想正式的接触一下,先跟着webpack的官方用户指南走: 在这里有: 如何安装webpack 如何使用webpack 如何使用loader 如何使用webpack的开发者 ...

  2. PHP-自定义模板-学习笔记

    1.  开始 这几天,看了李炎恢老师的<PHP第二季度视频>中的“章节7:创建TPL自定义模板”,做一个学习笔记,通过绘制架构图.UML类图和思维导图,来对加深理解. 2.  整体架构图 ...

  3. PHP-会员登录与注册例子解析-学习笔记

    1.开始 最近开始学习李炎恢老师的<PHP第二季度视频>中的“章节5:使用OOP注册会员”,做一个学习笔记,通过绘制基本页面流程和UML类图,来对加深理解. 2.基本页面流程 3.通过UM ...

  4. 2014年暑假c#学习笔记目录

    2014年暑假c#学习笔记 一.C#编程基础 1. c#编程基础之枚举 2. c#编程基础之函数可变参数 3. c#编程基础之字符串基础 4. c#编程基础之字符串函数 5.c#编程基础之ref.ou ...

  5. JAVA GUI编程学习笔记目录

    2014年暑假JAVA GUI编程学习笔记目录 1.JAVA之GUI编程概述 2.JAVA之GUI编程布局 3.JAVA之GUI编程Frame窗口 4.JAVA之GUI编程事件监听机制 5.JAVA之 ...

  6. seaJs学习笔记2 – seaJs组建库的使用

    原文地址:seaJs学习笔记2 – seaJs组建库的使用 我觉得学习新东西并不是会使用它就够了的,会使用仅仅代表你看懂了,理解了,二不代表你深入了,彻悟了它的精髓. 所以不断的学习将是源源不断. 最 ...

  7. CSS学习笔记

    CSS学习笔记 2016年12月15日整理 CSS基础 Chapter1 在console输入escape("宋体") ENTER 就会出现unicode编码 显示"%u ...

  8. HTML学习笔记

    HTML学习笔记 2016年12月15日整理 Chapter1 URL(scheme://host.domain:port/path/filename) scheme: 定义因特网服务的类型,常见的为 ...

  9. DirectX Graphics Infrastructure(DXGI):最佳范例 学习笔记

    今天要学习的这篇文章写的算是比较早的了,大概在DX11时代就写好了,当时龙书11版看得很潦草,并没有注意这篇文章,现在看12,觉得是跳不过去的一篇文章,地址如下: https://msdn.micro ...

  10. ucos实时操作系统学习笔记——任务间通信(消息)

    ucos另一种任务间通信的机制是消息(mbox),个人感觉是它是queue中只有一个信息的特殊情况,从代码中可以很清楚的看到,因为之前有关于queue的学习笔记,所以一并讲一下mbox.为什么有了qu ...

随机推荐

  1. Eclipse补全功能

    默认当输入 . 时会弹出提示补全, 如何设置 eclipse 代码自动补全,参考 http://jingyan.baidu.com/article/d45ad148b214a969552b8001.h ...

  2. 为何在有 DOCTYPE 的 HTML 文档之上仍然还会出现混杂模式?

    不使用 DOCTYPE 一定会使 HTML 文档处于混杂模式,然而使用了 DOCTYPE,也不一定就能够使文档在所有浏览器中均处于标准模式. DOCTYPE 本身不就是一个“开关”吗?为何在有 DOC ...

  3. JAVA基础--JAVA API集合框架(ArrayList、HashSet、HashMap使用)14

    一.集合Collection 1. 集合介绍 变量:表示的内存中的一个空间,只能保存确定类型的单个数据 数组:表示的是内存中的多个连续的空间,这些空间中可以存储多个同类型的数据. 后期继续学习面向对象 ...

  4. HDU 3499【最短路】

    题意: 给你一幅图,然后起点终点,然后有一个条件是可以使某条边的花费减半,求最短路的最小花费. 思路: (来自大哥) 最短路的时候多一维,途中是否有花费减半的边: 然后转移,如果上一条有减半的,这一条 ...

  5. [Xcode 实际操作]一、博主领进门-(13)在控制台的几种打印输出语句和po命令

    目录:[Swift]Xcode实际操作 本文将演几种在控制台输出日志的方式. 在项目导航区,打开视图控制器的代码文件[ViewController.swift] import UIKit class ...

  6. 【OpenJ_Bailian - 4005】拼点游戏(贪心)

    拼点游戏 Descriptions: C和S两位同学一起玩拼点游戏.有一堆白色卡牌和一堆蓝色卡牌,每张卡牌上写了一个整数点数.C随机抽取n张白色卡牌,S随机抽取n张蓝色卡牌,他们进行n回合拼点,每次两 ...

  7. day02 多态

  8. idea下载

  9. 洛谷 P4503 [CTSC2014]企鹅QQ

    暴力枚举不同的一位即可.. 主要是常数问题 1.统计答案时用sort速度快于用tr1/unordered_map,后者又快于map (tr1/unordered_map完全达不到理论复杂度上的O(1) ...

  10. 洛谷 P1086 花生采摘

    P1086 花生采摘 将植株按花生数从大到小排序,然后按排序后的顺序摘,每次摘前计算能否在摘后回到路边,如果能就将ans加上该植株花生数,如果不能就直接输出当前ans并退出. var a:array[ ...