makefile学习笔记(一)
第一章:概述
1.1:make概述
在linux环境下使用make工具能够比较容易的构建一个属于自己的工程,整个工程的编译只需要一个命令就可以完成编译、连接以至于最后的执行。不过我们需要投入一些时间去学习如何完成makefile文件的编写,这个文件也是make正常工作的基础。
所要完成的makefile文件描述了整个工程的编译、连接等规则。其中包括:工程中的哪些源文件需要编译以及如何编译、需要创建那些库文件以及如何创建这些库文件、如何最后产生我们想要的可执行文件。
make是一个命令工具,他解释了makefile中的指令。在makefile文件中描述了整个工程所有文件的编译顺序、编译规则。makefile有自己的书写格式,关键字、函数。想c语言有自己的格式、关键字和函数一样。而且在makefile中可以使用系统shell所提供的任何命令来完成想要的工作。
1.2:准备知识
在讨论make之前首先需要明确一些基本概念:
编译:把高级语言书写的代码转换为机器可识别的机器指令。编译后产生的机器指令虽然可被机器识别,但是还不能被执行。编译时,编译器检察高级语言的语法、函数与变量的声明是否正确。只有所有语法正确、相关变量定义正确,编译器就可以编译出中间目标文件。通常一个高级语言的源文件都可对应一个目标文件。目标文件在linux中默认后缀为“.o”。
链接:将多个目标文件,或者目标文件和库文件链接称为可被操作系统执行的可执行程序。链接器不检查函数所在的源文件,只检查所有目标文件的定义的符号。将目标文件使用的函数和其他目标或者库文件中的相关符号进行合并,并对所有文件中的符号进行重新安排,并连接系统相关文件最终生成可执行程序。
静态库:又称文档文件,十多个目标文件的集合。
共享库:也是多个目标文件的集合,但是这些目标文件按照一种特殊的方式生成。模块中各个成员的地址都是相对地址。使用此共享库的程序在运行时,共享库被动态加载到内存并和主程序在内存中进行链接。多个可执行程序可共享库文件中的代码段。
第二章:GUN make介绍
makemakefile告诉make以何种方式编译和链接程序,当某一文件更新时,make通过比较对应文件的最后修改时间,来决定那些文件需要更新,对需要的文件执行相应命令。
2.1:makefile简介:
当使用make工具进行编译时,工程中一些集中文件在执行make时将会被编译:
1、所有源文件中没有被编译过,则对各个c源文件进行编译并进行链接,生成最后的可执行程序;
2、每一个在上次执行make之后修改过的c源代码文件在本次执行make时将会被重新编译;
3、头文件在上一次执行make之后被修改。则所有包含此头文件的c源文件在本次执行make时将会被重新编译。
2.2:makefile规则介绍:
一个简单的makefile描述规则组成如下:
TARGET... : PREREQUITES
COMMAND
target:规则的目标,通常是最后需要生成的文件名或者为了实现这个目标而必须的中间过程文件名。可以是目标文件、也可以是最后的可执行程序的文件名。另外目标也可以使一个make执行的动作的名称,如clean,我们称这样的目标是伪目标。
prerequisites:规则的依赖。生成规则所需要的文件名列表。通常一个文件依赖于一个或者多个文件。
command:规则的命令行。是规则所要执行的动作(任意的shell命令或者是可在shell下执行的程序)。它限定了make执行这条规则时所需要的动作。
一个规则可以有多个命令行,每一条命令占一行。注意:每一个命令行必须一[tab]字符开始,[tab]字符告诉make此行是一个命令行。make按照命令完成相应的动作。这也是书写makefile中容易产生,而且比较隐蔽的错误。
命令就是任意一个目标的依赖文件发生变化后重建目标的动作描述。一个目标可以没有依赖而只有动作。
在makefile中的规则就是描述在什么情况下、如何重建规则的目标文件,通常规则中包括了目标的依赖关系和重建目标的命令。make执行重建目标的命令,来创建或者重建目标。规则包含了文件之间的依赖关系和更新此规则目标所需要的命令。
make程序根据规则的依赖关系,决定是够执行规则所定义的命令的过程我们称之为执行规则。
2.3:简单的示例
main.c:
#include"hello.h" int main(){
print();
return ;
}
hello.c:
#include"hello.h" void print(){
printf("hello world!\n");
}
hello.h:
#ifndef HELLO_H
#define HELLO_H #include<stdio.h>
void print(); #endif
makefile文件如下:
obj = main.o \
hello.o main: $(obj)
gcc $(obj) -o main
main.o: main.c hello.h
gcc -c main.c -o main.o
hello.o: hello.c hello.h
gcc -c hello.c -o hello.o
clean:
rm -rf *.o main
首先书写时,可以将一个较长行使用反斜杠分解为多行,使得更容易理解。但是需要注意:反斜杠之后不能有空格。在完成这个makefile以后;需要创建可执行程序main,所要做的就是在包含此makefile的目录下键入命令make。删除此目录下的使用make生成的文件,也只需要键入命令make clean就可以了。
命令行必须以tab键开始,以和makefile其他行区别。就是说所有的命令行必须以tab字符开始,但并不是所有的以tab字符开始的行都作为命令行来处理。(记住:make程序本身并不关心命令是如何工作的,对目标文件的更新需要你在规则描述中提供正确的命令。make程序所做的就是当目标程序需要更新时执行规则所定义的命令)。
目标clean不是一个文件,它仅仅代表执行一个动作的标识。正常情况下,不需要执行这个规则所定义的动作,因此目标clean没有出现在其他任何规则的依赖列表中。因此在执行make时,它所指定的动作不会被执行。除非在执行make时明确地指定它。而且目标clean没有任何依赖文件,他只有一个目的,就是通过这个目标名来执行它所定义的命令。makefile中把那些没有任何依赖只有执行动作的目标称为伪目标。
2.4:make如何工作
默认情况下,make执行的是makefile中的第一个规则,此规则的第一个目标称为最终目的或者终极目标。例如上例中的main文件。
当在shell中键入make命令后,make读取当前目录下的makefile文件,并将makefile文件中的第一个目标作为其执行的终极目标,开始处理第一个规则。在处理此规则所定义的命令之前,首先处理目标main所有的依赖文件的更新规则。这些目标文件为目标的规则的处理有下列三种情况:
1、目标文件不存在,使用其描述规则创建它;
2、目标文件存在,目标文件所以来的.c源文件或者头文件中的任何一个比目标文件更新。则根据规则重新编译生成它;
3、目标文件存在,目标文件比它的任何一个依赖文件更新,什么也不做。
在makefile中的一个规则的目标不是终极目标所依赖的,那么这个规则不会被执行,除非明确指定执行这个规则。
完成了对目标文件的创建或者更新之后,make程序将处理终极目标main所在的规则,分为以下三种情况:
1、目标文件不存在,则执行规则以创建目标main;
2、目标文件main存在,其依赖文件中有一个或者多个文件比它更新,则根据规则重新链接生成mian;
3、目标文件存在。它比它的任何一个依赖文件都新,则什么都不做。
总结对一个makefile文件,make首先解析终极目标所在的规则,根据其依赖文件依次寻找创建这些以来文件的规则。首先为第一个依赖文件寻找创建规则,如果第一个依赖文件依赖于其他文件,则同样为这个依赖文件寻找创建规则,知道为所有依赖文件找到合适的创建规则。之后make从最后一个规则回退开始执行,最终完成终极目标的第一个依赖文件的创建和更新。之后对第一个、第二个、第三个、。。。终极目标的依赖文件执行同样的过程。最后一步是创建此规则的目标。
更新终极目标的过程中,如果任何一个规则出现错误make就立即报错并退出。整个过程make只是负责执行规则,而对具体规则所描述的依赖关系的正确性、规则所定义的命令的正确性不做任何判断。就是说,一个规则的依赖关系是否正确、描述重建目标的规则命令行是够正确,make不做任何错误检查。因此编写一个正确的makefile文件爱你就显得尤为重要。
2.5:制定变量
上面的例子中的obj就是一个指定变量,下面每次使用时都可以直接使用;这样做不但减少书写的工作量,而且还可以减少修改而产生错误的可能。
2.6:自动推导规则
在使用make编译.c源文件时,编译.c源文件时,编译.c源文件规则的命令可以不用明确给出。这是因为make本身存在一个默认的规则,能够自动完成对.c文件的编译并生成对应的目标文件。他执行命令cc -c来编译.c文件。在makefile中我们只需要给出需要重建的目标文件名,meke会自动为这个目标文件寻找合适的依赖文件,并且使用正确的命令,来重建这个目标文件,对于上面的例子,此默认规则就是使用命令“cc -c main.c -o main.o”来创建文件'main.o'对一个目标文件。此默认规则称为make的隐含规则。
在书写时我们就可以省略掉描述重名的目标文件与依赖文件的规则,只需要给出那些特定的规则描述。因此上面的例子就可以更加简单的写成:
obj = main.o hello.o main: $(obj)
cc -o main $(obj)
main.o: hello.h
hello.o: hello.h
clean:
rm -rf *.o main
2.7:另类风格的makefile
我们也可以根据依赖而不是目标对规则进行分组。上例的makefile就可以这样来实现:
obj = main.o hello.o main : $(obj)
cc -o main $(obj)
#(obj) : hello.h
clean:
rm -rf *.o main
例子中hello.h作为所有.o文件的依赖文件。但这种书写方式不建议,后期维护会比较麻烦。
书写规则建议的方式是:单目标,多依赖。就是说尽量做到一个规则中只存在一个目标文件,可以有多个依赖文件。尽量避免多目标,单依赖的方式。
2.8:清除工作目录过程文件
规则除了完成源代码编译之外,也可以完成其他任务,例如前面提到的清除编译过程中产生的临时文件的规则。
clean:
rm -rf *.o main
在实际应用时,我们把这个规则写成如下稍微复杂一些的样子,以防止始料未及的情况。
.PHONY:clean
clean:
-rm -rf *.o main
这两个实现有两点不同:
1、通过“.PHONY”特殊目标将clean目标声明为伪目标。避免当磁盘上存在一个名为clean文件时,目标clean所在规则的命令无法执行。
2、在命令行之前使用-,意思是忽略命令“rm”的执行错误。
这样一个目标在makefile中,不能将其作为终极目标。因为我们的初衷并不是当你在命令行上输入make以后执行删除动作。而是要创建或者更新程序。
makefile学习笔记(一)的更多相关文章
- [转]Windows平台下Makefile学习笔记
Windows平台下Makefile学习笔记(一) 作者:朱金灿 来源:http://blog.csdn.net/clever101 决心学习Makefile,一方面是为了解决编译开源代码时需要跨编译 ...
- <转>Windows平台下Makefile学习笔记(二)
本文转自:http://blog.csdn.net/clever101/article/details/8286066 上次我们学习了怎么用Makefile编译一个控制台工程.这次我们学习一下如何使用 ...
- Makefile学习笔记
ls -l 查看文件详细信息 1.gcc -E test.c -o test.i//预编译gedit test.i //查看:高级C 2.gcc -Wall -S test.i -o test.s// ...
- makefile学习笔记(多目录嵌套调用、变量使用)
http://blog.csdn.net/leexiang_han/article/details/9274229 学习了几天的makefile的嵌套调用编写也有一些心得,先声明,我也是初学者写文 ...
- <转>Windows平台下Makefile学习笔记(一)
本文转自:http://blog.csdn.net/clever101/article/details/8147352 决心学习Makefile,一方面是为了解决编译开源代码时需要跨编译平台的问题(发 ...
- 【VS开发】Windows平台下Makefile学习笔记
作者:朱金灿 来源:http://blog.csdn.net/clever101 决心学习Makefile,一方面是为了解决编译开源代码时需要跨编译平台的问题(发现一些开源代码已经在使用VS2010开 ...
- makefile 学习笔记
1/ 编写简单makefile test_out: test.o g++ test.o -o test_out test.o: test.cpp test.h g++ -c test.cpp test ...
- Linux下Makefile学习笔记
makefile 可以用于编译和执行多个C/C++源文件和头文件. (1) #include "file.h" 和 #include <file.h> 的区别 #inc ...
- Makefile的学习笔记
Makefile的学习笔记 标签: makefilewildcard扩展includeshellfile 2012-01-03 00:07 9586人阅读 评论(2) 收藏 举报 分类: Linux ...
随机推荐
- Redis总结(六)Redis配置文件全解
前面已经写了一些关于redis 的介绍,redis 的基本功能和用法,基本上都说了,有问题的可以去看看 http://www.cnblogs.com/zhangweizhong/category/77 ...
- JDBC第二篇--【PreparedStatment、批处理、处理二进制、自动主键、调用存储过程、函数】
这是我JDBC的第一篇 http://blog.csdn.net/hon_3y/article/details/53535798 1.PreparedStatement对象 PreparedState ...
- MVC查询数据接收及校验
本来想写一篇aspx的TreeView控件绑值的文章的,在写案例的时候,写了一半,发现有些地方还得考虑以下,就留待下次了. 这一篇的话,是最近在开发一个项目的时候,有大量的页面和数据表,需要花式查询, ...
- angularjs之ui-bootstrap的Datepicker Popup实现双日期选择控件
最开始使用ui-bootstrap的Datepicker Popup日期选择插件实现双日期选择时间范围时,在网上搜了一些通过JS去实现的方法,不过后来发现可以不必通过JS去处理,只需要使用其自身的属性 ...
- pytorch实现DCGAN、pix2pix、DiscoGAN、CycleGAN、BEGAN以及VAE
https://github.com/sunshineatnoon/Paper-Implementations
- mysql技能提升篇 - Sqlyog高级应用
mysql作为绝大部分公司使用的数据库,自然是牛牛牛! 每个人都能设计数据库,都能从删库到跑路.但是,如何做到更好,更快,更准地建立你的mysql数据库,这是个值得关注的问题(尽管很多人已经去搞大数据 ...
- 51nod 1103 N的倍数 思路:抽屉原理+前缀和
题目: 这是一道很神奇的题目,做法非常巧妙.巧妙在题目要求n个数字,而且正好要求和为n的倍数. 思路:用sum[i]表示前i个数字的和%n.得到sum[ 1-N ]共N个数字. N个数字对N取模,每个 ...
- Egg + Vue 服务端渲染工程化实现
在实现 egg + vue 服务端渲染工程化实现之前,我们先来看看前面两篇关于Webpack构建和Egg的文章: 在 Webpack工程化解决方案easywebpack 文章中我们提到了基于 Vue ...
- 前端框架——AngularJS
前 言 AngularJS是一款为了克服HTML在构建应用上的不足而设计的优秀的前端JS框架.AngularJS有着诸多特性,最为核心的是:MVC.模块化.自动化双向数据绑定.语义化标签.依赖注 ...
- 和团队齐头并进——敏捷软件开发的Scrum的学习
敏捷开发的介绍 概念 更强调程序员团队与业务专家之间的紧密协作.面对面的沟通(认为比书面的文档更有效).频繁交付新的软件版本.紧凑而自我组织型的团队.能够很好地适应需求变化的代码编写和团队组织方法,也 ...