当前课程知识点:基于Linux的C++ > 第十二讲 Linux系统编程基础 > 12.14 makefile文件(三) > LinuxCPP1214
在编写 makefile 的时候
我们可以使用静态模式
用百分号来进行通配
它的基本格式是这样的:
“target : target_pattern : prerequisites”
第二行以后就用 Tab 键开头的 commands
你按照这个模式来写
它的最主要目的
就是用于处理模式相同的很多个目标
来简化我们脚本代码的
你比如讲
我有这样的一个程序需要编译
每一个目标的文件
都是以 “.o” 的形式存在的
先决条件都是以 “.c” 的形式来存在的
就像我们刚才那个例子
我就俩文件 “main.c”、“library.c”
还有一个头文件 “library.h” 对吧
总共就三文件 两个源文件
我要编译出来的目标文件其实就两个
“main.o”、“library.o”
这两个文件的格式是一模一样的
我不需要写两个 我写一个就行了
那么我就可以按照这个模式来写 makefile:
“objs = main.o library.o”
然后写一个规则 “$(objs) : %.o : %.c”
命令是 “$(CC) -c $(CFLAGS) $< -o $@”
什么东西 太怪了
把它翻译出来就是 “main.o : main.c”
“$(CC) -c $(CFLAGS) main.c -o main.o”
“library.o : library.c”
“$(CC) -c $(CFLAGS) library.c -o library.o”
明白了 我要生成两个目标文件
都以 “.o” 结尾
对应的先决条件的文件名字都是 “.c” 结尾
所以我们就可以把它统一成一行
就是对 $(objs) 这个变量里边的先决条件中的每一个
就是对 $(objs) 这个变量里边的先决条件中的每一个
它是个目标 $(objs) 这是一个目标了
那就是对这个目标中的每一个
因为它对应于 “main.o” 和 “library.o” 嘛
是双目标 我们这里面是多目标了
对于这多目标里面的每一个目标
“.o” 格式的目标
我们就找它对应的 “.c” 文件编译它
就一行就把它通配完了
用百分号来通配
这样的写法
当然要比写两行要方便一点
还记得我们刚才说了吧
“$<” 取的是它的第一个先决条件
如果它是 “main.o”
找它的第一个先决条件
就是 “main.c”
如果是 “library.o”
找它的第一个先决条件
就是 “library.c”
“$@” 就是它的目标名字 当前目标啊
生成的就是 “main.o”、“library.o”
每一个都是分解开来的当前的目标
就是这个意思
这是我们的静态模式
可以在 makefile 里面写条件判断
基本格式是条件指示
后面跟着一堆命令然后 endif
或者双路分支
条件指示之后跟着命令一串
else 命令一串 endif
就按照这个格式来书写我们的条件判断
就按照这个格式来书写我们的条件判断
可用的条件判断主要是四个
判断两个参数是不是相等
用 ifeq 问它是不是相等
小括号对里写 arg1, arg2
可以不加小括号对
用单引号引 arg1、arg2 也可以
用双引号引 arg1、arg2 也 OK
三种模式都可以
你要想判断两个参数不等那就 ifneq
实际上是 if not equal
如果不等 它的格式和 ifeq 是一样的
还可以判断某个变量是不是已经定义了
就用 ifdef
判断是不是没定义 ifndef 都可以
可以在 makefile 里边写循环
你比如讲
我们在这里面使用shell循环
定义一个伪目标 “rulefor :”
“for filename in `echo $(objs)`; \”
“do \” “rm –f $$filename; \” “done”
这是一个 shell 循环
写的时候要注意
循环是一个 shell 循环
为了保证这多行命令
可以在一个 shell 下面运行
那么你必须把它合并成一个单行的命令
每个单行的命令合并
前面不说了嘛 要用分号来分隔
你觉得写在一行 可读性不佳
那么你就可以用 “\”
在行尾输入一个折行标志
就可以分行写了
特别注意反引号表示执行这个命令
把这个命令执行后的结果
作为我们 for 循环的输入的集合
filename 就从 echo 这个命令做完了以后
出来的结果中去找
当你要在这个循环内部
想删除这个文件的时候
这个 filename 不是你定义的一个变量吗
这是在 shell 中定义的变量
因为在 shell 上面运行这个循环
所以这是 shell 变量
所以你要想在 makefile 引用它
必须使用 $$filename
不能使用 $(filename)
那是访问不到的 必须 $$filename
特别注意这一点
还可以在 makefile 里边使用函数
就像变量一样用 “$()”
它的基本格式是 “$( function arg1,arg2,…)”
有多少个参数 你就接着往下写就完了
写的时候就特别注意
函数名字和函数的参数都封在 “()” 里
函数名字和后面跟着参数列表中间
是有一个空格的
参数列表之间只有逗号没有空格
前面没空格后面也不能有空格
不能有多余的空格
特别注意这一条
有多余的空格
它会认为是参数的一部分
特别要小心
比如讲有一个函数叫 subst
处理子串的一个函数
这是 makefile 里面
已经预定义好的一个函数
我们可以直接用的
它后面带三个参数 from、to、text
什么意思呢 就是把 text
这个文本中的 from 那个字符串
那个子串 替换成 to 那个子串
这个函数做完了
它的结果就是替换后的那个字符串
我们看这样一个例子
我们定义一个逗号变量 comma
然后定义一个 empty 空值变量
它右边什么东西都没有 空值
注意它并不是一个空格 它是个空值
我们还想定义空格
一个空格 我们怎么定义呢
最简单的方式就是 space := $(empty) $(empty)
把它定义成两个空值变量
包着的一个空白
这样它在生成的时候
$(empty) 就什么东西也不生成
但是中间那个空格会保留下来
干嘛用的 你很快就能看到
假设我有一个变量叫 foo
它被定义成了 “a b c”
它可能对应我们三个文件的名字
也可能不是三个文件的名字
但是我想把 “a b c” 这个串
最终给它替换成 “a,b,c” 的模式
那儿你就应该这么写
bar := $(subst $(space),$(comma),$(foo))
就是把这个 foo 这个值 “a b c” 的里边
凡是空格的地方全都用 “,” 给我替换掉
所以出来的结果就是 “a,b,c”
就把它改了
这个就是 makefile 里面的函数的用法
很有趣
有的时候可能会给我们带来一些
非常好玩的结果
-1.1 提纲
-1.2 程序设计的基本概念
-1.3 简单C/C++程序介绍
-1.4 程序设计的基本流程
-1.5 基本语法元素
-1.6 程序设计风格
-1.7 编程实践
-第一讲 C/C++基本语法元素--编程实践提交入口
-2.1 提纲
-2.2 结构化程序设计基础
-2.3 布尔数据
-2.4 分支结构
-2.5 break语句
-2.6 循环结构
-2.7 编程实践
-第二讲 程序控制结构--编程实践提交入口
-3.1 提纲
-3.2 函数声明、调用与定义
-3.3 函数调用栈框架
-3.4 编程实践
-第三讲 函数--编程实践提交入口
-4.1 提纲
-4.2 算法概念与特征
-4.3 算法描述
-4.4 算法设计与实现
-4.5 递归算法(一)
-4.6 递归算法(二)
-4.7 容错与计算复杂度
-4.8 编程实践
-第四讲 算法--编程实践提交入口
-5.1 提纲
-5.2 库与接口
-5.3 随机数库(一)
-5.4 随机数库(二)
-5.5 作用域与生存期
-5.6 典型软件开发流程(一)
-5.7 典型软件开发流程(二)
-5.8 编程实践
-第五讲 程序组织与开发方法--编程实践提交入口
-6.1 提纲
-6.2 字符
-6.3 数组(一)
-6.4 数组(二)
-6.5 结构体
-6.6 编程实践
-第六讲 复合数据类型--编程实践提交入口
-7.1 提纲
-7.2 指针基本概念
-7.3 指针与函数
-7.4 指针与复合数据类型(一)
-7.5 指针与复合数据类型(二)
-7.6 字符串
-7.7 动态存储管理(一)
-7.8 动态存储管理(二)
-7.9 引用
-7.10 编程实践
-第七讲 指针与引用--编程实践提交入口
-8.1 提纲
-8.2 数据抽象(一)
-8.3 数据抽象(二)
-8.4 链表(一)
-8.5 链表(二)
-8.6 链表(三)
-8.7 链表(四)
-8.8 函数指针(一)
-8.9 函数指针(二)
-8.10 抽象链表(一)
-8.11 抽象链表(二)
-8.12 编程实践
-第八讲 链表与程序抽象--编程实践提交入口
-9.1 提纲
-9.2 程序抽象与面向对象
-9.3 类类型
-9.4 对象(一)
-9.5 对象(二)
-9.6 类与对象的成员(一)
-9.7 类与对象的成员(二)
-9.8 类与对象的成员(三)
-9.9 继承(一)
-9.10 继承(二)
-9.11 继承(三)
-9.12 多态(一)
-9.13 多态(二)
-9.14 编程实践
-第九讲 类与对象--编程实践提交入口
-10.1 提纲
-10.2 四则运算符重载(一)
-10.3 四则运算符重载(二)
-10.4 关系与下标操作符重载
-10.5 赋值操作符重载(一)
-10.6 赋值操作符重载(二)
-10.7 赋值操作符重载(三)
-10.8 赋值操作符重载(四)
-10.9 赋值操作符重载(五)
-10.10 流操作符重载(一)
-10.11 流操作符重载(二)
-10.12 流操作符重载(三)
-10.13 操作符重载总结
-10.14 编程实践
-第十讲 操作符重载--编程实践提交入口
-11.1 提纲
-11.2 泛型编程概览
-11.3 异常处理机制(一)
-11.4 异常处理机制(二)
-11.5 运行期型式信息(一)
-11.6 运行期型式信息(二)
-11.7 模板与型式参数化
-11.8 题外话:术语翻译
-11.9 泛型编程实践(一)
-11.10 泛型编程实践(二)
-11.11 泛型编程实践(三)
-11.12 泛型编程实践(四)
-11.13 泛型编程实践(五)
-11.14 泛型编程实践(六)
-11.15 泛型编程实践(七)
-11.16 泛型编程实践(八)
-11.17 泛型编程实践(九)
-11.18 泛型编程实践(十)
-11.19 编程实践
-第十一讲 泛型编程--编程实践提交入口
-12.1 提纲
-12.2 程序执行环境(一)
-12.3 程序执行环境(二)
-12.4 程序执行环境(三)
-12.5 程序执行环境(四)
-12.6 输入输出(一)
-12.7 输入输出(二)
-12.8 文件系统
-12.9 设备
-12.10 库(一)
-12.11 库(二)
-12.12 makefile文件(一)
-12.13 makefile文件(二)
-12.14 makefile文件(三)
-12.15 编程实践
-第十二讲 Linux系统编程基础--编程实践提交入口
-13.01 提纲
-13.02 进程基本概念
-13.03 信号
-13.04 进程管理(一)
-13.05 进程管理(二)
-13.06 进程管理(三)
-13.07 进程间通信(一)
-13.08 进程间通信(二)
-13.09 进程间通信(三)
-13.10 进程间通信(四)
-13.11 进程池
-13.12 编程实践
-第十三讲 进程编程--编程实践提交入口
-14.1 提纲
-14.2 线程基本概念
-14.3 线程管理(一)
-14.4 线程管理(二)
-14.5 线程管理(三)
-14.6 线程管理(四)
-14.7 线程同步机制(一)
-14.8 线程同步机制(二)
-14.9 C++11线程库(一)
-14.10 C++11线程库(二)
-14.11 C++11线程库(三)
-14.12 C++11线程库(四)
-14.13 C++11线程库(五)
-14.14 编程实践
-第十四讲 线程编程--编程实践提交入口
-15.1 提纲
-15.2 Internet网络协议
-15.3 套接字(一)
-15.4 套接字(二)
-15.5 编程实践
-第十五讲 网络编程--编程实践提交入口