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

  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. 反射invoke()方法

    invoke()方法: 主要是为了类反射,可以在不知道具体类的情况下,根据配置字符串去调用一个类的方法.最简单的方法是可以把方法参数化.    main.invoke():     比如Test类里有 ...

  2. JAVA基础--JAVA API常见对象(包装类和正则)12

    一.基本类型包装类 1.基本类型包装类介绍 8种基本类型: byte  short    int    long    float   double   char   boolean 这8种基本类型它 ...

  3. 洛谷 - P2424 - 约数和 - 整除分块

    https://www.luogu.org/problemnew/show/P2424 记 \(\sigma(n)\) 为n的所有约数之和,例如 \(\sigma(6)=1+2+3+6=12\) . ...

  4. MFC对话框程序 屏蔽ESC和ENTER键关闭对话框的方法

    http://blog.csdn.net/xgx198831/article/details/6713651 MFC对话框程序  屏蔽ESC和ENTER键关闭对话框的方法 或许还有其它更好的办法,但下 ...

  5. Unity2d 骨骼动画1:介绍

    http://bbs.9ria.com/thread-401613-1-1.html by Orlando Pereira and PedroPereira3 days ago2 Comments 在 ...

  6. 进击python第一篇:相遇

    1.第一句Python代码 在 当前目录下创建 hello.py 文件,内容如下: print "hello,world!" 执行 hello.py 文件,即: python he ...

  7. Codevs 1794 修剪花卉

    1794 修剪花卉   题目描述 Description ZZ对数学饱有兴趣,并且是个勤奋好学的学生,总是在课后留在教室向老师请教一些问题. 一天他早晨骑车去上课,路上见到一个老伯正在修剪花花草草,顿 ...

  8. [Xcode 实际操作]九、实用进阶-(14)使用富文本CoreText框架创建丰富多彩的文本

    目录:[Swift]Xcode实际操作 本文将演示如何使用富文本CoreText框架创建丰富多彩的文本图形. 在项目导航区,打开视图控制器的代码文件[ViewController.swift] imp ...

  9. IT兄弟连 JavaWeb教程 JSP中的注释

    由于JSP页面由HTML.JSP.Java脚本等组成,所以在其中可以使用多种注释格式 HTML中的注释 HTML语言的注释不会被显示在网页中,但是在浏览器中选择查看网页源代码时,还是能够看到注释的信息 ...

  10. Trie树(小)总结 By cellur925

    关于\(Trie\)树的详细介绍,还请移步这篇深度好文 基本操作 插入 void insert() { int p=0; int len=strlen(tmp+1); for(int i=1;i< ...