mac linux makefile,Makefile简单入门
最近工作编译程序一直在用别人写的Makefile,但是没有系统的学习过,趁着放假学一波
makefile
0x00 Makefile 概述
一个企业级项目,通常会有很多源文件,有时也会按功能、类型、模块分门别类的放在不同的目录中,有时候也会在一个目录里存放了多个程序的源代码。
这时,如何对这些代码的编译就成了个问题。Makefile 就是为这个问题而生的,它定义了一套规则,决定了哪些文件要先编译,哪些文件后编译,哪些文件要重新编译。
整个工程通常只要一个 make 命令就可以完成编译、链接,甚至更复杂的功能。可以说,任何一个 Linux 源程序都带有一个Makefile 文件。
0x01 Makefile 的优点
管理代码的编译,决定该编译什么文件,编译顺序,以及是否需要重新编译;节省编译时间。如果文件有更改,只需重新编译此文件即可,无需重新编译整个工程;一劳永逸。Makefile 通常只需编写一次,后期就不用过多更改。
0x02 编译知识
Makefile最初是为了编译C/C++而诞生的, 所以它里面的很多隐藏规则都是针对 C/C++的。在讲 Makefile 之前有必要对 C/C++的编译有一点了解
过程如下:
compile
预处理器:将.c 文件转化成 .i 文件,使用的 gcc 命令是:gcc –E,对应于预处理命令 cpp;
编译器:将.c/.h 文件转换成.s 文件,使用的 gcc 命令是:gcc –S,对应于编译命令 cc –S;
汇编器:将.s 文件转化成 .o 文件,使用的 gcc 命令是:gcc –c,对应于汇编命令是 as;
链接器:将.o 文件转化成可执行程序,使用的 gcc 命令是: gcc,对应于链接命令是 ld;
加载器:将可执行程序加载到内存并进行执行,loader 和 ld-linux.so。
0x03 Makefile规则
Target...: Prerequsites...
Command
Command
...
或
Targets: Prerequisites;Command
Command
...
下面会称 Target 为目标, Prerequisites 为目标依赖, Command 为规则的命令行
Command 必须以[Tab]开始, Command 可以写成多行,通过来继行,但行尾的后不能有空格。
规则包含了文件之间的依赖关系和更新此规则 target 所需要的 Command
targets 可以使用通配符, 如果格式是"A(M)"表示档案文件(.a)中的成员“M”
在需要用本义的时候,使用两个$$来表示。
当规则的 target 是一个文件,它的任何一个依赖文件被修改后,在执行 make 时这个目标文件都会被重新编译或重新连接。如果有必要此 target 的一个依赖文件也会被先重新编译。
0x04伪目标
Makefile 中把那些没有任何依赖只有执行动作的目标称为“伪目标“(Phony targets)
.PHONY : clean
clean :
-rm edit $(objects
通过.PHONY 将 clean 声明为伪目标,避免当目录下有名为“clean”文件时,clean 无法执行
这样的目标不是为了创建或更新程序,而是执行相应动作。
0x05自动推导规则
在使用 make 编译.c 源文件时,编译.c 源文件规则的命令可以不用明确给出。这是因为 make 本身存在一个默认的规则,能够自动完成对.c 文件的编译并生成对应的.o 文件。它执行命令“cc -c”来编译.c 源文件。在 Makefile 中我们只需要给出需要重建的目标文件名(一个.o 文件),make 会自动为这个.o 文件寻找合适的依赖文件(对应的.c 文件。对应是指:文件名除后缀外,其余都相同的两个文件),而且使用正确的命令来重建这个目标文件。
例如, 现在有三个文件 test.cpp, my.cpp, my.h
image.png
test.cpp
#include
#include "my.h"
int main(int argc, char * argv[]) {
int a = 100, b = 101;
std::cout << "this code is for test makefile" << std::endl;
std::cout << xadd(a, b) << std::endl;
}
my.h
#ifndef _MY_H_
#define _MY_H_
int xadd(const int x, const int y);
#endif
my.cpp
#include "my.h"
int xadd(const int x, const int y)
{
return x + y;
}
对于上边的例子,此默认规则就使用命令“gcc -c test.cpp -o test.o”来创建文件“main.o”。对一个目标文件是“N.o”,倚赖文件是“N.c”的规则,完全可以省略其规则的命令行,而由 make 自身决定使用默认命令。此默认规则称为 make 的隐含规则。
test: test.cpp my.o
gcc -c -o test test.cpp
my.o: my.cpp my.h
gcc -c -o my.o my.cpp
clean :
rm test my.o
也可以用隐式规则
test: test.cpp my.o
my.o: my.cpp my.h
clean :
rm test my.o
效果是一样的
这里要说明一点的是, clean 不是一个文件,它只不过是一个动作名字,有点像c语言中的label一 样,其冒号后什么也没有,那么,make就不会自动去找它的依赖性,也就不会自动执行其后所定义的命令。 要执行其后的命令,就要在make命令后明显得指出这个label的名字。这样的方法非常有用,我们可以在一 个makefile中定义不用的编译或是和编译无关的命令,比如程序的打包,程序的备份,等等。
0x06 规则书写建议
书写规则建议的方式是:单目标,多依赖。就是说尽量要做到一个规则中只存在一个目标文件,可有多个依赖文件。尽量避免使用多目标,单依赖的方式。
0x07 makefile 工作原理文和件搜索顺序
在默认的方式下,也就是我们只输入 make 命令。那么,
首先会搜索目录下的GNUmakefile,makefile,Makefile文件,或者make -f从指定文件读取
2.找到makefile后首先从第一个target开始,如果生成target依赖别的目标就递归从依赖开始
例如:上面的例子中,首先准备编译生成目标test,发现依赖my.o没有生成,就向下找my.o的生成,发现my.o的资源my.cpp,my.h已经就绪了,就先编译出my.o,回到test,发现 test.cpp和my.o全部就绪,使用规则Command生成目标test
这就是整个make的依赖性,make会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件。在 找寻的过程中,如果出现错误,比如最后被依赖的文件找不到,那么make就会直接退出,并报错,而对于所 定义的命令的错误,或是编译不成功,make根本不理。make只管文件的依赖性,即,如果在我找了依赖关系 之后,冒号后面的文件还是不在,那么对不起,我就不工作啦。
通过上述分析,我们知道,像clean这种,没有被第一个目标文件直接或间接关联,那么它后面所定义的命 令将不会被自动执行,不过,我们可以显示要make执行。即命令—— make clean ,以此来清除所有 的目标文件,以便重编译。
0x08 makefile中使用变量
我们可以看到 .o 文件的字符串被重复了两次,如果我们的工程需要加入一个新的 .o 文件, 那么我们需要在两个地方加(应该是三个地方,还有一个地方在clean中)。
当然,我们的makefile并不复 杂,所以在两个地方加也不累,但如果makefile变得复杂,那么我们就有可能会忘掉一个需要加入的地方, 而导致编译失败。所以,为了makefile的易维护,在makefile中我们可以使用变量。makefile的变量也 就是一个字符串,理解成C语言中的宏可能会更好。
比如,我们声明一个变量 obj,表示所有obj文件,在makefile的一开始就定义
obj = my.o
maincpp = test.cpp
于是,我们就可以很方便地在我们的makefile中以 $(obj) 的方式来使用这个变量了,于是 我们的改良版makefile就变成下面这个样子:
obj = my.o
maincpp = test.cpp
test :$(maincpp) $(obj)
my.o: my.cpp my.h
clean:
rm $(obj)
于是如果有新的 .o 文件加入,我们只需简单地修改一下 obj 变量就可以了。
关于变量更多的话题,我会在后续给你一一道来。
0x09 另类风格的makefiles
既然我们的make可以自动推导命令,那么我看到那堆.o 和 .h 的依赖就有点不爽,那么多的重复的 .h ,能不能把其收拢起来,好吧,没有问题,这个对于make来说很容易,谁叫它提供了自动 推导命令和文件的功能呢?来看看最新风格的makefile吧。
obj = my.o
maincpp = test.cpp
test :$(maincpp) $(obj)
$(obj): my.h
clean:
rm $(obj) test
这种风格,让我们的makefile变得很简单,但我们的文件依赖关系就显得有点凌乱了。鱼和熊掌不可兼得。 还看你的喜好了。我是不喜欢这种风格的,一是文件的依赖关系看不清楚,二是如果文件一多,要加入几个 新的.o 文件,那就理不清楚了。
0x10 清空目标文件的规则
每个Makefile中都应该写一个清空目标文件( .o 和执行文件)的规则,这不仅便于重编译,也很 利于保持文件的清洁。这是一个“修养”(呵呵,还记得我的《编程修养》吗)。一般的风格都是:
clean:
rm test $(obj)
更为稳健的做法是:
.PHONY: clean
clean:
rm test $(obj)
0x11 Makefile的文件名
默认的情况下,make命令会在当前目录下按顺序找寻文件名为“GNUmakefile”、 “makefile”、“Makefile”的文件,找到了解释这个文件。在这三个文件名中,最好使用“Makefile” 这个文件名,因为,这个文件名第一个字符为大写,这样有一种显目的感觉。最好不要用“GNUmakefile”, 这个文件是GNU的make识别的。有另外一些make只对全小写的“makefile”文件名敏感,但是基本上来说, 大多数的make都支持“makefile”和“Makefile”这两种默认文件名。
当然,你可以使用别的文件名来书写Makefile,比如:“Make.Linux”,“Make.Solaris” ,“Make.AIX”等,如果要指定特定的Makefile,你可以使用make的-f和--file参数
make -f Makefile.Linux
make -f Makefile.mac
0x12 引用其他的Makefile
在Makefile使用include 关键字可以把别的Makefile包含进来,这很像C语言的 #include ,被包含的文件会原模原样的放在当前文件的包含位置。 include 的语法是:
include
filename 可以是当前操作系统Shell的文件模式(可以包含路径和通配符)。
在include 前面可以有一些空字符,但是绝不能是 Tab 键开始。 include 和 可以用一个或多个空格隔开。举个例子,你有这样几个Makefile: a.mk 、 b.mk 、 c.mk ,还有一个文件叫 foo.make ,以及一个变量 $(bar) ,其包含 了 e.mk 和 f.mk ,那么,下面的语句:
include foo.make *.mk $(bar)
等价于
include foo.make a.mk b.mk c.mk e.mk f.mk
如果make执行时,有 -I 或 --include-dir 参数,那么make就会在这个参数所指定的目 录下去寻找。
2.如果目录 /include (一般是: /usr/local/bin 或 /usr/include )存在的话,make也会去找。
如果有文件没有找到的话,make会生成一条警告信息,但不会马上出现致命错误。它会继续载入其它的 文件,一旦完成makefile的读取,make会再重试这些没有找到,或是不能读取的文件,如果还是 不行,make才会出现一条致命信息。如果你想让make不理那些无法读取的文件,而继续执行,你可以 在include前加一个减号“-”。如:
-include
0x13 环境变量MAKEFILES
如果你的当前环境中定义了环境变量 MAKEFILES ,那么,make会把这个变量中的值做一个类似于 include 的动作。这个变量中的值是其它的Makefile,用空格分隔。只是,它和 include 不 同的是,从这个环境变量中引入的Makefile的“目标”不会起作用,如果环境变量中定义的文件发现 错误,make也会不理。
但是在这里我还是建议不要使用这个环境变量,因为只要这个变量一被定义,那么当你使用make时, 所有的Makefile都会受到它的影响,这绝不是你想看到的。在这里提这个事,只是为了告诉大家,也许 有时候你的Makefile出现了怪事,那么你可以看看当前环境中有没有定义这个变量。
0x14 变量定义及赋值:
变量直接采用赋值的方法即可完成定义,如:
INCLUDE = ./include/
变量取值:
用括号括起来再加个美元符,如:
`FOO = $(OBJ)`
系统自带变量:
通常都是大写,比如 CC、PWD、CFLAG,等等。
有些有默认值,有些没有。比如常见的几个:
CPPFLAGS : 预处理器需要的选项 如:-I
CFLAGS:编译的时候使用的参数 –Wall –g -c
LDFLAGS :链接库使用的选项 –L -l
变量的默认值可以修改,比如 CC 默认值是 cc,但可以修改为 gcc:CC=gcc
0x15 函数
Makefile 也为我们提供了大量的函数,同样经常使用到的函数为以下两个。需要注意的是,Makefile 中所有的函数必须都有返回值。在以下的例子中,假如目录下有 main.c、func1.c、func2.c 三个文件。
通配符:
用于查找指定目录下指定类型的文件,跟的参数就是目录+文件类型,比如:
src = $(wildcard ./src/*.c)
这句话表示:找到 ./src 目录下所有后缀为 .c 的文件,并赋给变量 src。
命令执行完成后,src 的值为:main.c func1.c fun2.c。
patsubst:
匹配替换,例如以下例子,用于从 src 目录中找到所有 .c 结尾的文件,并将其替换为 .o 文件,并赋值给 obj。
obj = $(patsubst %.c ,%.o ,$(src))
命令执行完成后,obj 的值为 main.o func1.o func2.o。
特别地,如果要把所有 .o 文件放在 obj 目录下,可用以下方法:
obj = $(patsubst ./src/%.c, ./obj/%.o, $(src))
mac linux makefile,Makefile简单入门相关推荐
- linux下Makefile中包含有shared library动态链接库文件时候的简单例子
如果不知道什么是makefile,可以首先看我的另一篇博客: linux下Makefile的简单例子及解释 http://www.cnblogs.com/lihaozy/archive/2012/08 ...
- linux make教程,Linux下makefile的一个简单框架
目录结构 tree . |-- Makefile `-- src |-- Makefile |-- bar | |-- Makefile | `-- bar.c `-- foo |-- Makefil ...
- Linux平台Makefile文件的编写基础篇和GCC参数详解
问:gcc中的-I.是什么意思....看到了有的是gcc -I. -I/usr/xxxxx..那个-I.是什么意思呢 最佳答案 答:-Ixxx 的意思是除了默认的头文件搜索路径(比如/usr/incl ...
- Linux基本命令+Makefile
1.linux下查看进程占用cpu的情况(top): 格式 top [-] [d delay] [q] [c] [S] [s] [i] [n] 主要参数 d:指定更新的间隔,以秒计算. q:没有任何延 ...
- Linux内核Makefile文件
Linux内核Makefile文件(翻译自内 核手册) 转载自:http://blog.chinaunix.net/uid-21651676-id-60377.html Linux 内核Makefil ...
- Linux下Makefile的automake生成全攻略(转)
Linux下Makefile的automake生成全攻略(转)[@more@] 文/余涛 作为Linux下的程序开发人员,大家一定都遇到过Makefile,用make命令来编译自己写的程序确实是很方便 ...
- mac退出linux命令行,mac/Linux日常入门命令行使用
8种机械键盘轴体对比 本人程序员,要买一个写代码的键盘,请问红轴和茶轴怎么选? Linux/mac 命令行操作简单介绍 linux/mac 日常入门命令行使用--文件以及文件夹操作 日常文件夹以及文件 ...
- Linux下Makefile编写语法
原创 Linux下Makefile编写语法 2016-07-29 08:31:53 Datrilla 阅读数 1386更多 分类专栏: Linux Makefile 版权声明:本文为博主原创文章,遵循 ...
- Linux内核Makefile
注:以下文字大部分来自韦东山<嵌入式Linux应用开发完全手册> Linux内核源码中含有很多个Makefile文件主要分为以下5类: Linux内核Makefile文件分类 顶层Make ...
最新文章
- 我去,你写的 switch 语句也太老土了吧
- 此字符不允许在标识符中使用_计算机中C语言的-基本语法
- mysql权限表整理
- R语言观察日志(part16)--Google‘s R Style Guide
- c# WebApi之接口返回类型详解
- Unreal Engine 4 —— 多线程任务构建
- Java读取Properties文件的六种方法
- matlab中给图像加几个矩形框_如何用 matlab 在图片上绘制矩形框 和 添加文字 ?...
- python xlwt_python的xlwt模块
- 微信小程序云函数发天气预报
- Steam 游戏服务器IP地址段
- openharmony常用网站
- ERROR: failed to establish dependency between database sgerp5 and diskgroup resource ora.DATA.dg
- 记一次 unicode-escape 和 utf-8 编码的互解
- linux笔记本设置开机自动禁用触摸板
- 【通用】Windows Server 2012突然无法复制文件到阿里云服务器
- 关于JDK8发送邮件失败的问题
- linux 使用 nginx 搭建 zookeeper 集群
- jq的ajax上传文件
- 可视化例子(14)——ECharts波浪图(或者称为河流图)
热门文章
- getAttribute()、setAttribute()、removeAttribute()的方法使用以及区别。
- 一场为13条公链,5万多种通证赋能的全球开发者大赛正式开始!一等奖30万!...
- 信息学奥赛一本通1038 苹果与虫子 计蒜客C1 苹果与虫子
- 2018年蓝桥杯省赛B组C++真题
- SharePoint 2010 主题
- MATLAB算法实战应用案例精讲-【自动驾驶】毫米波雷达(补充篇)
- 鸿蒙微内核基于,华为发布基于微内核、面向全场景的分布式操作系统:鸿蒙OS...
- Qt编写自定义控件插件路过的坑及注意事项
- 如何快速的搭建一个个人博客网站
- win7下硬盘安装Ubuntu 12.04