为什么要用 make

先来想象一个案例,假设我的执行档里面包含了四个原始码文件,分别是 main.c haha.c sin_value.c cos_value.c 这四个文件,这四个文件的目的是:

  • main.c :主要的目的是让用户输入角度数据与呼叫其他三支子程序
  • haha.c :输出一堆有的没有的讯息而已;
  • sin_value.c :计算使用者输入的角度(360) sin 数值;
  • cos_value.c :计算使用者输入的角度(360) cos 数值。
# 1. 先进行目标文件的编译,最终会有四个 *.o 的档名出现:
[root@study ~]# gcc -c main.c
[root@study ~]# gcc -c haha.c
[root@study ~]# gcc -c sin_value.c
[root@study ~]# gcc -c cos_value.c # 2. 再进行连结成为执行档,并加入 libm 的数学函式,以产生 main 执行档:
[root@study ~]# gcc -o main main.o haha.o sin_value.o cos_value.o -lm # 3. 本程序的执行结果,必须输入姓名、360 度角的角度值来计算
[root@study ~]# ./main
Please input your name: VBird <==这里先输入名字
Please enter the degree angle (ex> 90): 30 <==输入以 360 度角为主的角度
Hi, Dear VBird, nice to meet you. <==这三行为输出的结果喔!
The Sin is: 0.50
The Cos is: 0.87

编译的过程需要进行好多动作啊!而且如果要重新编译,则上述的流程得要重新来一遍,光是找出这 些指令就够烦人的了! 如果可以的话,能不能一个步骤就给他完成上面所有的动作呢?那就利用 make 这个工具吧! 先试看看在这个目录下建立一个名为 makefile 的文件,内容如下:

# 1. 先编辑 makefile 这个规则文件,内容只要作出 main 这个执行档
[root@study ~]# vim makefile
main: main.o haha.o sin_value.o cos_value.o
gcc -o main main.o haha.o sin_value.o cos_value.o -lm
# 注意:第二行的 gcc 之前是 <tab> 按键产生的空格喔! # 2. 尝试使用 makefile 制订的规则进行编译的行为:
[root@study ~]# rm -f main *.o <==先将之前的目标文件去除
[root@study ~]# make
cc -c -o main.o main.c
cc -c -o haha.o haha.c
cc -c -o sin_value.o sin_value.c
cc -c -o cos_value.o cos_value.c
gcc -o main main.o haha.o sin_value.o cos_value.o -lm
# 此时 make 会去读取 makefile 的内容,并根据内容直接去给他编译相关的文件啰! # 3. 在不删除任何文件的情况下,重新执行一次编译的动作:
[root@study ~]# make
make: `main' is up to date.
# 看到了吧!是否很方便呢!只会进行更新 (update) 的动作而已。

或许你会说:『如果我建立一个 shell script 来将上面的所有动作都集结在一起,不是具有同样的效 果吗?』效果当然不一样,以上面的测试为例,我们仅写出 main 需要的目标文件,结果 make会主动的去判断每个目标文件相关的原始码文件,并直接予以编译,最后再直接进行连结的动作!真 的是很方便啊!此外,如果我们更动过某些原始码文件,则 make 也可以主动的判断哪一个原始码 与相关的目标文件文件有更新过, 并仅更新该文件,如此一来,将可大大的节省很多编译的时间呢!所以说, make 有这些好处:

  • 简化编译时所需要下达的指令;
  • 若在编译完成之后,修改了某个原始码文件,则 make 仅会针对被修改了的文件进行编译,其他的 object file不会被更动;
  • 最后可以依照相依性来更新 (update) 执行档。

既然 make 有这么多的优点,那么我们当然就得好好的了解一下 make 这个令人关心的家伙啦!而 make 里面最需要注意的大概就是那个规则文件,也就是 makefile 这个文件的语法啦!所以底下我 们就针对 makefile 的语法来加以介绍啰。

makefile 的基本语法与变量

make 的语法可是相当的多而复杂的,这里 仅列出一些基本的规则,基本的 makefile 规则是这样的:

目标(target): 目标文件 1 目标文件 2
<tab> gcc -o 欲建立的执行文件 目标文件 1 目标文件 2

那个目标 (target) 就是我们想要建立的信息,而目标文件就是具有相关性的 object files ,那建立执 行文件的语法就是以 按键开头的那一行!特别给他留意喔,『命令行必须要以 tab 按键作为

开头』才行!他的规则基本上是这样的:

  • 在 makefile 当中的 # 代表批注;
  • 需要在命令行 (例如 gcc 这个编译程序指令) 的第一个字符;
  • 目标 (target) 与相依文件(就是目标文件)之间需以『:』隔开。

同样的,我们以刚刚上一个小节的范例进一步说明,如果我想要有两个以上的执行动作时, 例如下 达一个指令就直接清除掉所有的目标文件与执行文件,该如何制作呢?

# 1. 先编辑 makefile 来建立新的规则,此规则的目标名称为 clean :
[root@study ~]# vi makefile
[root@study ~]# vi makefile
main: main.o haha.o sin_value.o cos_value.o
gcc -o main main.o haha.o sin_value.o cos_value.o -lm
clean:
rm -f main main.o haha.o sin_value.o cos_value.o # 2. 以新的目标 (clean) 测试看看执行 make 的结果:
[root@study ~]# make clean <==就是这里!透过 make 以 clean 为目标
rm -rf main main.o haha.o sin_value.o cos_value.o

如此一来,我们的 makefile 里面就具有至少两个目标,分别是 main 与 clean ,如果我们想要建立 main 的话,输入『make main』,如果想要清除有的没的,输入『make clean』即可啊!而如果想要 先清除目标文件再编译 main 这个程序的话,就可以这样输入:『make clean main』,如下所示:

[root@study ~]# make clean main
rm -rf main main.o haha.o sin_value.o cos_value.o
cc -c -o main.o main.c
cc -c -o haha.o haha.c
cc -c -o sin_value.o sin_value.c
cc -c -o cos_value.o cos_value.c
gcc -o main main.o haha.o sin_value.o cos_value.o -lm

这样就很清楚了吧!但是,你是否会觉得,咦! makefile 里面怎么重复的数据这么多啊!没错!所 以我们可以再藉由 shell script 那时学到的『变数』来更简化 makefile 喔:

[root@study ~]# vi makefile
LIBS = -lm
OBJS = main.o haha.o sin_value.o cos_value.o
main: ${OBJS}
gcc -o main ${OBJS} ${LIBS}
clean:
rm -f main ${OBJS}

与 bash shell script 的语法有点不太相同,变量的基本语法为:

  1. 变量与变量内容以『=』隔开,同时两边可以具有空格;
  2. 变量左边不可以有 ,例如上面范例的第一行 LIBS 左边不可以是 ;
  3. 变量与变量内容在『=』两边不能具有『:』;
  4. 在习惯上,变数最好是以『大写字母』为主;
  5. 运用变量时,以 ${变量} 或 $(变量) 使用;
  6. 在该 shell 的环境变量是可以被套用的,例如提到的 CFLAGS 这个变数!
  7. 在指令列模式也可以给予变量。

由于gcc 在进行编译的行为时,会主动的去读取 CFLAGS 这个环境变量,所以,你可以直接在 shell 定义出这个环境变量,也可以在 makefile 文件里面去定义,更可以在指令列当中给予这个咚咚呢! 例如:

[root@study ~]# CFLAGS="-Wall" make clean main
# 这个动作在上 make 进行编译时,会去取用 CFLAGS 的变量内容!

也可以这样:

[root@study ~]# vi makefile
LIBS = -lm
OBJS = main.o haha.o sin_value.o cos_value.o
CFLAGS = -Wall
main: ${OBJS}
gcc -o main ${OBJS} ${LIBS}
clean:
rm -f main ${OBJS}

我可以利用指令列进行环境变量的输入,也可以在文件内直接指定环境变量,那万一这个 CFLAGS 的内容在指令列与 makefile 里面并不相同时,以那个方式输入的为主?呵呵!问了个好问 题啊! 环境变量取用的规则是这样的:

  1. make 指令列后面加上的环境变量为优先;
  2. makefile 里面指定的环境变量第二;
  3. shell 原本具有的环境变量第三。

此外,还有一些特殊的变量需要了解的喔:

  • $@:代表目前的目标(target)

所以也可以将 makefile 改成:

[root@study ~]# vi makefile
LIBS = -lm
OBJS = main.o haha.o sin_value.o cos_value.o
CFLAGS = -Wall
main: ${OBJS}
gcc -o $@ ${OBJS} ${LIBS} <==那个 $@ 就是 main !
clean:
rm -f main ${OBJS}

Linux学习-用 make 进行宏编译的更多相关文章

  1. Linux学习-以最新核心版本编译 CentOS 7.x 的核心

    为了某些缘故需要最新的 4.x.y 的核心版本来实作某些特定的功能时,那该 如何是好?没办法,只好使用最新的核心版本来编译你可以依照上面的程序来一个一个处理, 没有问题~不过,你也可以根据 ELRep ...

  2. Linux学习-额外(单一)核心模块编译

    编译前注意事项 由于我们的核心原本就有提供很多的核心工具给硬件开发商来使用, 而硬件开发商也需要针对核心 所提供的功能来设计他们的驱动程序模块,因此, 我们如果想要自行使用硬件开发商所提供的模块 来进 ...

  3. 【Linux学习】Ubuntu下内核编译(一)

    (1)当要运行内核配置时,输入make menuconfig时出现错误 遇到这个问题,主要是以为没有ncurses库,而make menconfig 须要这个库.因此须要安装ncurses或者ncur ...

  4. Linux学习系列八:操作网口

    一些相对高性能的单片机会带以太网接口,网口在MCU里算是比较复杂的外设了,因为它涉及到网络协议栈,通常情况下网络协议栈会运行在一个RTOS中,所以对普通单片机开发者来说网口使用起来相对难度较大一些.在 ...

  5. #define中 #与##的神奇用法linux学习 (转)

    #define中 #与##的神奇用法linux学习 (转) #define f(a,b) a##b #define d(a) #a #define s(a) d(a) void main( void ...

  6. linux学习书籍推荐《鸟哥的Linux私房菜》下载

    下载地址:点我 <鸟哥的Linux私房菜:基础学习篇>是具有知名度的Linux入门书<鸟哥的Linux私房菜基础学习篇>的最新版,全面而详细地介绍了Linux操作系统.< ...

  7. Linux 学习笔记

    Linux学习笔记 请切换web视图查看,表格比较大,方法:视图>>web板式视图 博客园不能粘贴图片吗 http://wenku.baidu.com/view/bda1c3067fd53 ...

  8. Linux学习心得之 Linux下命令行Android开发环境的搭建

    作者:枫雪庭 出处:http://www.cnblogs.com/FengXueTing-px/ 欢迎转载 Linux学习心得之 Linux下命令行Android开发环境的搭建 1. 前言2. Jav ...

  9. Linux学习心得之 Linux下ant安装与使用

    作者:枫雪庭 出处:http://www.cnblogs.com/FengXueTing-px/ 欢迎转载 Linux学习心得之 Linux下ant安装与使用 1. 前言2. ant安装3. 简单的a ...

随机推荐

  1. ms sqlserver 登录失败 错误:4064

    无法打开用户默认数据库.登录失败.用户‘sa’登录失败.(Microsoft SQL Server, 错误:4064) 解决方法:解决方法:先用windows身份验证的方式登录进去,然后在 安全性=& ...

  2. NHibernate中创建User类报错问题

    前两天刚开始学习NHibernate架构,照着前辈的例子打了一遍运行之后没问题,然后自己创建了一个User的Model发现一运行就报User附近有错误,然后就检查,类写的没错用了virtual,Use ...

  3. Random类、ThreadLocalRandom类

    Random和ThreadLocalRandom类均用于生成伪随机数. Random的构造函数: Random()     默认以系统当前时间为种子,相当于Random(System.currentT ...

  4. PHP实现正态分布的累积概率函数

    在实际项目中,遇到需要正态分布算法去计算一个数值在整体的分布区间,例如:  100,90,80,70,60,50,40,30,20,10共10个数,按从高到低的顺序排序,总数的10%分布区域为极高频, ...

  5. 【Python图像特征的音乐序列生成】一个更科学的图片分类参考方法,以及一个看起来很好用的数据集

    数据集地址:http://www.imageemotion.org/ 论文地址:http://www.doc88.com/p-1905670442096.html

  6. UVA 10891 Game of Sum (决策优化)

    这是一个零和博弈,最高得分只和序列以及谁先手有关. d[i][j],表示i到j的序列当前取的这个人的最高得分,转移以后状态是新的区间和另一个人取,从中取最小值. 决策的最小值也可递推. #includ ...

  7. [论文理解] Connectionist Text Proposal Network

    Connectionist Text Proposal Network 简介 CTPN是通过VGG16后在特征图上采用3*3窗口进行滑窗,采用与RPN类似的anchor机制,固定width而只预测an ...

  8. 用dfs求解八皇后问题

    相信大家都已经很熟悉八皇后问题了,就是指:在8X8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行.同一列或同一斜线上,问有多少种摆法.主要思路:按行进行深度优先搜索,在该 ...

  9. 剑指offer58 二叉树的下一个结点

    自己写的 class Solution { public: TreeLinkNode* GetNext(TreeLinkNode* pNode) { if(pNode == NULL) return ...

  10. thinkphp 为什么访问路径错误,还可以访问

    在学习中访问入口文件,实际上应该访问public\index\index\   但其实也可以访问application.admin.controller\index,同样可以在网页下显示 原理:pub ...