当前课程知识点:基于Linux的C++ >  第十二讲 Linux系统编程基础 >  12.12 makefile文件(一) >  LinuxCPP1212

返回《基于Linux的C++》慕课在线视频课程列表

LinuxCPP1212在线视频

LinuxCPP1212

下一节:LinuxCPP1213

返回《基于Linux的C++》慕课在线视频列表

LinuxCPP1212课程教案、知识点、字幕

这一讲的最后一节 makefile

makefile 是一个文件

它专门用来指导编译我们的程序用的

它专门用来指导编译我们的程序用的

过去在 Linux 下边没有集成开发环境

我们写的 C 程序也好 C++ 程序也好

都需要通过命令行的手段对它进行编译

都需要通过命令行的手段对它进行编译

当这个文件 当这个程序很大

文件很多的时候

那么手工地去编译它

实际上是非常费劲的

所以为了方便程序员

就可以写一个 makefile

然后用 make 这个命令去调用它

make 命令就负责我们 C/C++ 代码的编译与链接

make 命令就负责我们 C/C++ 代码的编译与链接

它会去读取这个 makefile 文件中

里面的内容

根据它的内容去执行你的命令

完成整个程序的编译和链接

makefile 做的就是这个事情

它就是给出 make 命令

编译和链接我们程序时的规则

在 Linux 操作系统下面

makefile 的文件有三个命名

叫 GNUmakefile

小写的 makefile 和大写 Makefile

“M” 大写 三个名字都可以

不过我建议同学们用后边两个

因为第一个 GNUmakefile

是 GNU 专用的

如果你写的代码那个不是 GNU 的

那么它可能就不能用

makefile 有自己的固定的文件格式

也有自己的语法规则

它能够处理基本的语法元素

能够支持变量 能够进行条件判断

能够写循环 还能够写函数

你看 它实际上

本质上就是一个编程语言了 很复杂

makefile 在早期写 C/C++ 的代码的时候

非常非常有用

但现在同学们有了集成开发环境

makefile 的使用

频率就不像过去那么多

但是对于一个 Linux 程序员来讲

了解 makefile 还是很有必要的

所以我们这一节

就会简单地介绍 makefile

makefile 文件格式是固定的:

“target : prerequisites 换行 Tab 键 commands”

必须按照这个方式来

makefile 本身是由一系列的规则来构成的

规则是干嘛用的

就是我们建构目标的先决条件是什么

以及怎么样来完成这个目标的建构

target 就是我们的建构目标

prerequisites 就是我们的建构先决条件

commands 就是它的建构命令

因为它是一个行格式的

makefile 里面所有的内容

都是以行的形式来进行解析的

所以它的格式特别要求

所有的命令前是要有 Tab 键的

冒号前是它的目标

冒号后是建构这个目标

所需要的先决条件

下边紧跟着的一行或多行

就成了它的建构命令

格式是固定的

你如果没有指定目标

那么 make 在运行的时候

就会执行这个 makefile 里边的缺省的第一目标

就会执行这个 makefile 里边的缺省的第一目标

第一目标就是它缺省的

你没有指定 它就用它的第一个目标

如果第一个目标能够解决 它就会解决

如果第一个目标还需要其它的目标才能够解决

如果第一个目标还需要其它的目标才能够解决

那么它就会不断地回溯

所以它实际上是构造了一系列的规则链

这样的一个描述构造一个规则

下面可能再构造另外一个规则

它会形成一个规则链

如果 prerequisites 有一个以上的文件比 target 新

如果 prerequisites 有一个以上的文件比 target 新

下面的命令它就会去执行

以确保我们的 target 目标是有效的

是最新的 就是这个意思

target 就是我们的目标

通常情况下

它应该是编译期的一个文件名

我们指定我们要创建的这个目标

要建构的对象是什么

那就是一个文件名

我们要生成什么样的文件

这就是我们的 target

这是一般情况

事实上它可以是个执行文件

还可以纯粹地就是一个标签

它是一个操作的名字

它不对应于真实的文件

这种东西我们称它为伪目标

target 这个地方可以写一个目标

也可以写很多个目标

这个没关系

对于规则的构建来讲

targets 那是一系列的目标

很多个 target 可以写在一起

中间有一个空格来分隔开 就完了

它后面的先决条件也可以有好多个

中间也是用空格来分隔开的

特别需要说明的是

每一个那样的 targets 目标集

它其实都构成了一组处理规则

它决定了那个目标或者那些目标

如何被构造

当那个目标在这个规则下

不能被构造的时候

就是它还需要其它的先决条件

才能构造我们的先决条件的时候

那么它就会找那个先决条件的构成的规则 如果那个构成规则还需要其它构成规则

那么它就会找那个先决条件的构成的规则 如果那个构成规则还需要其它构成规则

那么它就会找那个先决条件的构成的规则 如果那个构成规则还需要其它构成规则

它会继续去找其它的构成规则

这就意味着在查找 makefile 文件的时候

它会形成一个规则链

这一点是需要特别去注意的

prerequisites 就是它的先决条件

为生成我们这个目标所需要的一些文件或者一些目标

为生成我们这个目标所需要的一些文件或者一些目标

那就是先决文件或先决目标

有时我们称之为前置条件

你得有了它 才能构建我们的目标

prerequisites 要做的事其实就是这个东西

prerequisites 要做的事其实就是这个东西

一般的情况下呢

它是一些空格分隔的一些文件名

它指定我们的目标

建构的时候的判断标准

我们应该从什么地方

来建构我们的目标 就这个意思

只要有一个先决文件 它不是最新的

那么我们就需要重建我们的目标

target 就需要被重建

makefile 最主要的规则就在这里

对于 prerequisites 这个先决条件来讲

如果这个先决条件本身需要被重建

那么它就会匹配先决条件的那个对应的目标规则

那么它就会匹配先决条件的那个对应的目标规则

然后就执行对应的命令

commands 就是命令

由一行或多行 shell 命令来组成的

每一个命令前都有一个 Tab 键

你说为了美观

我前面用两个 Tab 键可不可以呢 可以

我用 Tab 键和空格可不可以呢 也可以

但是你要记得 Tab 键必须是开头的

顶头字符必须是 Tab 键 不能是空格

特别注意这一条

target 那个目标头前面是不能有 Tab 键的

但是为了美观 你也可以缩进

但那个时候只能用空格去缩进

不能使用 Tab 键缩进

Tab 键只能用在命令行的前面

这是第一个需要注意到的

所有的命令都是为了指示

我们如何建构目标的

一般是生成我们的目标文件所需要的命令序列

一般是生成我们的目标文件所需要的命令序列

我第一步做什么 第二步做什么

第三步做什么

它是好多条命令合成的

注意这一点

特别需要说明的是

每行命令

都是在一个单独的 shell 里运行的

也就是说它们运行在不同的进程里

彼此之间是没有继承关系的

所以你不能够简单地

在两条命令中间传递数据

不可以的

可以在 shell 里定义变量

可以用 shell 变量来传递数据

但是特别要记住

两行命令 它们是在不同的 shell

不同的进程里面运行的

你直接简单地认为它们可以传递数据

可以共用一些信息是不妥当的

解决方法当然也是有的

如果你真地需要它们共享信息

你可以把这多条命令

把它合并成一条

每条命令中间用分号分隔开

后面命令紧跟着写在那一行

就是把这两条命令合并成一条

让它们在一个 shell 下面运行

如果这一行很长 你觉得写不下

那么你可以在行尾用 “\” 来折行

把后面那个部分写在第二行

“\” 折行 这是允许的

还有一种方案

当它很长的时候 你也不想折行

你又想分行写 那怎么办呢

你可以把整个这个规则

前面加上一个 “.ONESHELL :”

加这个指示就表示把所有这些命令都在一个 shell 里面把它运行完

加这个指示就表示把所有这些命令都在一个 shell 里面把它运行完

就是这个意思

你按照这个方式书写 command

如果一个目标后边不带先决条件

那么它就表示一个伪目标

仅仅是一个操作的名称

而不是一个文件名

你比如讲 如果我在编译完成之后

我想删除编译期生成的一些二进制的目标文件

我想删除编译期生成的一些二进制的目标文件

就是中间结果我不想要了

我就可以写一个伪目标 clean

后面跟着一个命令 “rm –f *.o”

把所有 “.o” 的文件全都删掉

我不要了

我可执行文件都编译出来

我还要 “.o” 干嘛呢

不要了 OK

我就可以 “rm –f *.o” 把它删掉

写了一个伪目标 “clean :”

这就是它的目标

当你执行编译命令的时候

你就可以执行 make

后面带参数 clean

它就会执行这个目标

调用 rm 命令把所有的 “.o” 文件全部删除

调用 rm 命令把所有的 “.o” 文件全部删除

这就是伪目标

但是你要注意

当这个当前目录下有一个文件

名字本身就叫 “clean” 的时候

你 make clean 就有可能导致问题

它就会去 make 你那 clean 那个文件去了

为了确保不会出现文件查找的情况

那么你可以在 clean 的前边

填上一个特殊的指示 “.PHONY : clean”

表示 clean 明确的是一个伪目标

而不是一个文件

那么 make 就会跳过文件查找过程

直接执行你的伪目标

按照这个方式去书写

有一点是需要说明的

clean 这个伪目标一般放在我们 makefile 文件的末尾

clean 这个伪目标一般放在我们 makefile 文件的末尾

放在前面 尤其放在最顶头

它会作为缺省目标的

所以一般我们把它放在末尾

表示做最后的扫尾工作

在 Linux 操作系统下

有一些伪目标的定义惯例

你并不需要一定按照这个方式

但是如果整个程序员社区

基本上都是按照这个模式

来书写伪目标的名字

那么你不按照这个方式来写

它本身就有点奇怪

让别人就不太容易看得懂

你的 makefile 到底写的是什么

所以正常情况下

我们应该遵照这样的惯例

比如说伪目标 all

它就用来表示所有目标的目标

当你一个工程项目

包括很多个可执行文件

想要同时编译的时候

你就可以一个伪目标 all

将所有的文件一股脑、一口气全部都编译完

将所有的文件一股脑、一口气全部都编译完

这就是 all 目标做的事

clean 就是删除它的中间文件

install 就是安装

print 就是输出我们

改变过的源文件的信息

tar 就是对它备份

dist 就不仅仅备份

我还要形成一个压缩包 以利于分发

你比如说我们看这样一个例子

假设我们有一个程序

主文件名字叫 main.c

它需要使用到 library 这个库

这个库当然也是我们写的

包括 “.h” 和 “.c” 这两个文件

所以我们就可以按照这个模式写

我们的目标就叫 prog 冒号

它需要使用到的两个先决条件

就是 “main.o” 和 “library.o”

两个目标文件

我要用它来构造我们的可执行程序

就这个意思 我们怎么构造呢

当然调用 cc 编译器 “-o prog”

就是输出文件名叫 “prog”

然后需要使用 “main.o”、“library.o”

这两个目标文件来链接它

“main.o” 怎么来呢

当然要编译 “main.c”、“library.h”

“main.c” 需要使用到 “library.h”

所以我们按照这个模式写

编译的时候就是 “cc –c main.c”

这是它的编译命令

那么 “library.o” 这个文件怎么来呢

它的先决条件就是 “library.c”、“library.h”

然后写 “.PHONY : clean”

“clean :” “rm main.o library.o”

把这两个目标文件清除掉就完了

这个就是我们的 makefile 文件

然后你在命令行下就可以运行 make

它就会执行缺省的第一条命令

替我们生成我们的 prog 文件

如果你执行 make clean

它就会把所有的 “.o”

都给你清除掉

删除 没了

基于Linux的C++课程列表:

第一讲 C/C++基本语法元素

-1.1 提纲

--LinuxCPP0101

-1.2 程序设计的基本概念

--LinuxCPP0102

-1.3 简单C/C++程序介绍

--LinuxCPP0103

-1.4 程序设计的基本流程

--LinuxCPP0104

-1.5 基本语法元素

--LinuxCPP0105

-1.6 程序设计风格

--LinuxCPP0106

-1.7 编程实践

--LinuxCPP0107

-第一讲 C/C++基本语法元素--编程实践提交入口

第二讲 程序控制结构

-2.1 提纲

--LinuxCPP0201

-2.2 结构化程序设计基础

--LinuxCPP0202

-2.3 布尔数据

--LinuxCPP0203

-2.4 分支结构

--LinuxCPP0204

-2.5 break语句

--LinuxCPP0205

-2.6 循环结构

--LinuxCPP0206

-2.7 编程实践

--LinuxCPP0207

-第二讲 程序控制结构--编程实践提交入口

第三讲 函数

-3.1 提纲

--LinuxCPP0301

-3.2 函数声明、调用与定义

--LinuxCPP0302

-3.3 函数调用栈框架

--LinuxCPP0303

-3.4 编程实践

--LinuxCPP0304

-第三讲 函数--编程实践提交入口

第四讲 算法

-4.1 提纲

--LinuxCPP0401

-4.2 算法概念与特征

--LinuxCPP0402

-4.3 算法描述

--LinuxCPP0403

-4.4 算法设计与实现

--LinuxCPP0404

-4.5 递归算法(一)

--LinuxCPP0405

-4.6 递归算法(二)

--LinuxCPP0406

-4.7 容错与计算复杂度

--LinuxCPP0407

-4.8 编程实践

--LinuxCPP0408

-第四讲 算法--编程实践提交入口

第五讲 程序组织与开发方法

-5.1 提纲

--LinuxCPP0501

-5.2 库与接口

--LinuxCPP0502

-5.3 随机数库(一)

--LinuxCPP0503

-5.4 随机数库(二)

--LinuxCPP0504

-5.5 作用域与生存期

--LinuxCPP0505

-5.6 典型软件开发流程(一)

--LinuxCPP0506

-5.7 典型软件开发流程(二)

--LinuxCPP0507

-5.8 编程实践

--LinuxCPP0508

-第五讲 程序组织与开发方法--编程实践提交入口

第六讲 复合数据类型

-6.1 提纲

--LinuxCPP0601

-6.2 字符

--LinuxCPP0602

-6.3 数组(一)

--LinuxCPP0603

-6.4 数组(二)

--LinuxCPP0604

-6.5 结构体

--LinuxCPP0605

-6.6 编程实践

--LinuxCPP0606

-第六讲 复合数据类型--编程实践提交入口

第七讲 指针与引用

-7.1 提纲

--LinuxCPP0701

-7.2 指针基本概念

--LinuxCPP0702

-7.3 指针与函数

--LinuxCPP0703

-7.4 指针与复合数据类型(一)

--LinuxCPP0704

-7.5 指针与复合数据类型(二)

--LinuxCPP0705

-7.6 字符串

--LinuxCPP0706

-7.7 动态存储管理(一)

--LinuxCPP0707

-7.8 动态存储管理(二)

--LinuxCPP0708

-7.9 引用

--LinuxCPP0709

-7.10 编程实践

--LinuxCPP0710

-第七讲 指针与引用--编程实践提交入口

第八讲 链表与程序抽象

-8.1 提纲

--LinuxCPP0801

-8.2 数据抽象(一)

--LinuxCPP0802

-8.3 数据抽象(二)

--LinuxCPP0803

-8.4 链表(一)

--LinuxCPP0804

-8.5 链表(二)

--LinuxCPP0805

-8.6 链表(三)

--LinuxCPP0806

-8.7 链表(四)

--LinuxCPP0807

-8.8 函数指针(一)

--LinuxCPP0808

-8.9 函数指针(二)

--LinuxCPP0809

-8.10 抽象链表(一)

--LinuxCPP0810

-8.11 抽象链表(二)

--LinuxCPP0811

-8.12 编程实践

--LinuxCPP0812

-第八讲 链表与程序抽象--编程实践提交入口

第九讲 类与对象

-9.1 提纲

--LinuxCPP0901

-9.2 程序抽象与面向对象

--LinuxCPP0902

-9.3 类类型

--LinuxCPP0903

-9.4 对象(一)

--LinuxCPP0904

-9.5 对象(二)

--LinuxCPP0905

-9.6 类与对象的成员(一)

--LinuxCPP0906

-9.7 类与对象的成员(二)

--LinuxCPP0907

-9.8 类与对象的成员(三)

--LinuxCPP0908

-9.9 继承(一)

--LinuxCPP0909

-9.10 继承(二)

--LinuxCPP0910

-9.11 继承(三)

--LinuxCPP0911

-9.12 多态(一)

--LinuxCPP0912

-9.13 多态(二)

--LinuxCPP0913

-9.14 编程实践

--LinuxCPP0914

-第九讲 类与对象--编程实践提交入口

第十讲 操作符重载

-10.1 提纲

--LinuxCPP1001

-10.2 四则运算符重载(一)

--LinuxCPP1002

-10.3 四则运算符重载(二)

--LinuxCPP1003

-10.4 关系与下标操作符重载

--LinuxCPP1004

-10.5 赋值操作符重载(一)

--LinuxCPP1005

-10.6 赋值操作符重载(二)

--LinuxCPP1006

-10.7 赋值操作符重载(三)

--LinuxCPP1007

-10.8 赋值操作符重载(四)

--LinuxCPP1008

-10.9 赋值操作符重载(五)

--LinuxCPP1009

-10.10 流操作符重载(一)

--LinuxCPP1010

-10.11 流操作符重载(二)

--LinuxCPP1011

-10.12 流操作符重载(三)

--LinuxCPP1012

-10.13 操作符重载总结

--LinuxCPP1013

-10.14 编程实践

--LinuxCPP1014

-第十讲 操作符重载--编程实践提交入口

第十一讲 泛型编程

-11.1 提纲

--LinuxCPP1101

-11.2 泛型编程概览

--LinuxCPP1102

-11.3 异常处理机制(一)

--LinuxCPP1103

-11.4 异常处理机制(二)

--LinuxCPP1104

-11.5 运行期型式信息(一)

--LinuxCPP1105

-11.6 运行期型式信息(二)

--LinuxCPP1106

-11.7 模板与型式参数化

--LinuxCPP1107

-11.8 题外话:术语翻译

--LinuxCPP1108

-11.9 泛型编程实践(一)

--LinuxCPP1109

-11.10 泛型编程实践(二)

--LinuxCPP1110

-11.11 泛型编程实践(三)

--LinuxCPP1111

-11.12 泛型编程实践(四)

--LinuxCPP1112

-11.13 泛型编程实践(五)

--LinuxCPP1113

-11.14 泛型编程实践(六)

--LinuxCPP1114

-11.15 泛型编程实践(七)

--LinuxCPP1115

-11.16 泛型编程实践(八)

--LinuxCPP1116

-11.17 泛型编程实践(九)

--LinuxCPP1117

-11.18 泛型编程实践(十)

--LinuxCPP1118

-11.19 编程实践

--LinuxCPP1119

-第十一讲 泛型编程--编程实践提交入口

第十二讲 Linux系统编程基础

-12.1 提纲

--LinuxCPP1201

-12.2 程序执行环境(一)

--LinuxCPP1202

-12.3 程序执行环境(二)

--LinuxCPP1203

-12.4 程序执行环境(三)

--LinuxCPP1204

-12.5 程序执行环境(四)

--LinuxCPP1205

-12.6 输入输出(一)

--LinuxCPP1206

-12.7 输入输出(二)

--LinuxCPP1207

-12.8 文件系统

--LinuxCPP1208

-12.9 设备

--LinuxCPP1209

-12.10 库(一)

--LinuxCPP1210

-12.11 库(二)

--LinuxCPP1211

-12.12 makefile文件(一)

--LinuxCPP1212

-12.13 makefile文件(二)

--LinuxCPP1213

-12.14 makefile文件(三)

--LinuxCPP1214

-12.15 编程实践

--LinuxCPP1215

-第十二讲 Linux系统编程基础--编程实践提交入口

第十三讲 进程编程

-13.01 提纲

--LinuxCPP1301

-13.02 进程基本概念

--LinuxCPP1302

-13.03 信号

--LinuxCPP1303

-13.04 进程管理(一)

--LinuxCPP1304

-13.05 进程管理(二)

--LinuxCPP1305

-13.06 进程管理(三)

--LinuxCPP1306

-13.07 进程间通信(一)

--LinuxCPP1307

-13.08 进程间通信(二)

--LinuxCPP1308

-13.09 进程间通信(三)

--LinuxCPP1309

-13.10 进程间通信(四)

--LinuxCPP1310

-13.11 进程池

--LinuxCPP1311

-13.12 编程实践

--LinuxCPP1312

-第十三讲 进程编程--编程实践提交入口

第十四讲 线程编程

-14.1 提纲

--LinuxCPP1401

-14.2 线程基本概念

--LinuxCPP1402

-14.3 线程管理(一)

--LinuxCPP1403

-14.4 线程管理(二)

--LinuxCPP1404

-14.5 线程管理(三)

--LinuxCPP1405

-14.6 线程管理(四)

--LinuxCPP1406

-14.7 线程同步机制(一)

--LinuxCPP1407

-14.8 线程同步机制(二)

--LinuxCPP1408

-14.9 C++11线程库(一)

--LinuxCPP1409

-14.10 C++11线程库(二)

--LinuxCPP1410

-14.11 C++11线程库(三)

--LinuxCPP1411

-14.12 C++11线程库(四)

--LinuxCPP1412

-14.13 C++11线程库(五)

--LinuxCPP1413

-14.14 编程实践

--LinuxCPP1414

-第十四讲 线程编程--编程实践提交入口

第十五讲 网络编程

-15.1 提纲

--LinuxCPP1501

-15.2 Internet网络协议

--LinuxCPP1502

-15.3 套接字(一)

--LinuxCPP1503

-15.4 套接字(二)

--LinuxCPP1504

-15.5 编程实践

--LinuxCPP1505

-第十五讲 网络编程--编程实践提交入口

课程文档

-课程PDF文件

LinuxCPP1212笔记与讨论

也许你还感兴趣的课程:

© 柠檬大学-慕课导航 课程版权归原始院校所有,
本网站仅通过互联网进行慕课课程索引,不提供在线课程学习和视频,请同学们点击报名到课程提供网站进行学习。