当前课程知识点:基于Linux的C++ > 第十二讲 Linux系统编程基础 > 12.13 makefile文件(二) > LinuxCPP1213
makefile 在书写的时候要按照它自己的语法规范来写
makefile 在书写的时候要按照它自己的语法规范来写
它和 C 的代码是不一样的
很多时候它和 shell 那个编程规范非常的相似
很多时候它和 shell 那个编程规范非常的相似
首先它是行解析的
所有的命令都是按行解析的
所以命令行的行首必须是 Tab 键 不能是空格
所以命令行的行首必须是 Tab 键 不能是空格
目标那一行 前面可以写空格
注释可以写空格
但是命令行本身必须以 Tab 键来开头
其它行是不可以出现 Tab 键的
可以用空格 但不能用 Tab 键
命令行太长的时候可以换行
但是行尾一定要有 “\” 标记
这一点和宏定义
C/C++ 代码里面的宏定义是一致的
行尾要用 “\” 来换行
所有以 “#” 开头的文本行都是注释
没有 “#” 就不是注释
第一个字符必须是 “#”
它就是注释
make 在执行我们的 makefile 的时候
它会首先输出 makefile 里边的那行命令的全部的内容
它会首先输出 makefile 里边的那行命令的全部的内容
然后才会执行我们的命令
当你不需要显示那行命令的内容的时候
当你不需要显示那行命令的内容的时候
你就可以关闭命令的回显
只要在命令前加上一个 “@” 标记
就表示关闭这个命令的回显
我们一般是不关闭命令的回显的
只在纯粹的注释或者纯粹地显示 echo 信息的时候
只在纯粹的注释或者纯粹地显示 echo 信息的时候
才会在前面关闭它的回显
makefile 本身可以包含其它的文件
关键字就用 include
“include filename” 就会包含另外一个 makefile 文件
“include filename” 就会包含另外一个 makefile 文件
把其它文本文件包含进来
包含到我们的 makefile 文件里就可以
包含到我们的 makefile 文件里就可以
包含文件有可能会出错
如果你想让它忽略这个文件包含错误
如果你想让它忽略这个文件包含错误
就在 include 前面加一个减号 “-include”
就表示包含那个 filename 的时候如果出错
就表示包含那个 filename 的时候如果出错
你就忽略这个错误继续往下去做
如果不加 make 就会停
在 makefile 支持通配符
像 “*” 它就能够通配任意数目的任意字符
像 “*” 它就能够通配任意数目的任意字符
像 “?” 它就能够通配任意一个字符
像 “?” 它就能够通配任意一个字符
像 “[abc]” 它就能够通配 “[]” 里面的任意一个字符
像 “[abc]” 它就能够通配 “[]” 里面的任意一个字符
像 “[0-9]” 就表示能够通配 “0” 到 “9” 之间的任何一个字符
像 “[0-9]” 就表示能够通配 “0” 到 “9” 之间的任何一个字符
像 “[^abc]” 就通配任何一个不在这个中括号里面的 “a”、“b”、“c” 那三个字符之一
像 “[^abc]” 就通配任何一个不在这个中括号里面的 “a”、“b”、“c” 那三个字符之一
像 “[^abc]” 就通配任何一个不在这个中括号里面的 “a”、“b”、“c” 那三个字符之一
我们可以在 makefile 里面定义变量
定义的格式跟我们的环境变量差不多
variable_name = value
前面是变量的名字
后面是变量的值
就是有的 shell
它定义变量的时候
等号前后不允许加空格
而在我们的 makefile 里边
这里是可以加空格的
引用这个变量必须按照 “$(变量名称)”
按照这样的方式引用这个变量
有时可以不写 “()”
但大部分的时候我们都写 “()”
但是你要特别注意
“()” 和变量名称的中间
是不允许有多余的空格的
有多余的空格编译就会出错了
如果你想引用的是一个 shell 变量
那么一个 “$” 不够用
要用两个 “$$” 才够用
你比如讲 我要回显 $$HOME
就按照这个模式操作
变量在使用时展开
形式上类似于宏替换
特别注意这一点
事实上不管是变量的名字
还是变量的值
make 在解释这个 makefile 的时候
都是把它当做字符串来处理的
所以形式上 它相当于宏文本替换
这一点是需要特别注意的
在 makefile 里边
变量可以使用在几乎所有的场合
目标可以用 先决条件可以用
命令可以用
还可以用它定义新的变量
没问题 都可以用
makefile 里边有一些内置的变量
我们可以直接用
比如说 “$(CC)”
这就是表示我们当前使用的编译器是哪一个
这就是表示我们当前使用的编译器是哪一个
“$(MAKE)” 表示我们当前使用的 make 工具是哪一个
“$(MAKE)” 表示我们当前使用的 make 工具是哪一个
链接工具是哪一个
还有一些预定义的自动变量
比如讲 “$@” 就是当前目标
“$<” 就是当前目标的首个先决条件
都是一些符号表示
看上去都是一些江湖黑话一样的东西
不太好理解 也不太好记忆
这个没辙
包括 shell 编程里面用到的很多东西都是这个模样
包括 shell 编程里面用到的很多东西都是这个模样
写起来呢会简单 但是对于外行
尤其是刚开始学的时候
你理解它的时候 就觉得高大上
蛮深奥的东西 不明觉厉的样子
但实际上你把这个东西
帮助手册朝那一放
一对照 你就明白它在写什么东西了
基本上是这样的一个模式
shell 编程是这样
makefile 也是这个意思
记住就行了 没别的什么好招
我们看这样的一个例子
我定义一个变量 objs
objs 它的值是什么
“main.o library.o”
注意 这是它的值
我们 “prog : $(objs)”
这就是它的先决条件
我要想构建 prog 这个目标
先决条件就是 “$(objs)”
是什么 就是 “main.o library.o”
我们就会 “$(CC) -o prog $(objs)”
我们回显 “Constructed…”
已构造完成
“main.o” 怎么得到呢
“$(CC) -c main.c” “library.o” 同样
“.PHONY : clean” “rm -f prog $(objs)js *~”
最后我把它全删光
只保留了源程序
可执行文件被我给删掉了
中间文件 “.o” 一系列的 “.o”
都被我删掉了
然后所有生成的备份文件
以 “~” 结尾的备份文件
也被我删除掉了
全删掉了 只留下三个文件
“main.c”、“library.c”、“library.h”
其它文件全部被我删光
clean 做的就这个事
这就是我们的 makefile 文件
你看 “#” 开头那一行
那个就是注释
实际在执行的时候
它调用 makefile 的文件
那么它就会将 “main.o library.o”
替换这里面 “$(objs)”
然后根据替换出来的结果
执行我们的命令
在 makefile 里面
变量的定义可不是刚才的那一种形式
变量的定义可不是刚才的那一种形式
事实上有四种定义变量的模式
一个是变量的名字等于值
这个变量的定义意味着这个变量将在执行期展开
这个变量的定义意味着这个变量将在执行期展开
展开的时候 它是允许递归的
也就是说 可能使用后续代码才会出现的 value 值
也就是说 可能使用后续代码才会出现的 value 值
如果你不想让它在执行期扩展
而在定义时就扩展它
不让它递归
那么你就使用 “:=” 格式对它进行定义
那么你就使用 “:=” 格式对它进行定义
这个时候它将使用右侧那个 value 的现值来定义这个变量
这个时候它将使用右侧那个 value 的现值来定义这个变量
这个时候它将使用右侧那个 value 的现值来定义这个变量
在这个现值后来发生改变的时候
变量的内容不会变的
第一种变量的内容
会跟着 value 的值发生变化的
第二种定义格式就不会变化
还有一种就是 “?=” 赋值方式
只有在这个变量为空的时候
它才会设定这个变量的值为 value
否则的话它会忽略掉
它就维持原值不变
还有一种是 “+=”
就是 “+=”
把 value 给它附加到原来变量的尾部
我们不讲了吗
不管这个变量还是这个变量的值
名字也好 值也好
在命令行里实际上都是字符串
它就要把后面的这个东西
添加到原来那个变量值的尾部
如果这个变量没有定义
那么 “+=” 就自动地变成等号
如果这个变量已经定义好了
那么 “+=” 就会自动地变成
你上次定义的那个操作符
把这个东西追加到了尾部
你原先使用 “=” 来定义的
那么这个“+=”就意味着把这个东西
追加到原来那个东西的尾部
使用 “=” 追加
如果你原来使用 “:=” 来对它进行赋值的
如果你原来使用 “:=” 来对它进行赋值的
那么 “+=” 的话就意味着使用 “:=” 把它追加过去
那么 “+=” 的话就意味着使用 “:=” 把它追加过去
使用现值追加过去 就这一点差别
特别需要注意
可以在 makefile 里面定义多行的变量
比如说 define var_name
一行命令接着一行命令
最后 endef 结束这个定义
这就是几行变量
那么这个变量的名字就用这个变量值 就是底下这几行命令
那么这个变量的名字就用这个变量值 就是底下这几行命令
一般情况下面
我们定义多行变量的意义
主要是用于定义命令包
我们平时是很少用的
所以使用的时候
是需要特别特别小心的
为啥呢 还是我刚才讲的 对吧
因为这个东西
它可能是在不同的 shell 下面运行的
所以得到的结果互相之间可能没关系
展开以后 脚本是非常有可能导致错误的
所以用的时候是要特别小心
平时我们一般很少用它
特别需要说明 define 和 endef 本身不是命令
特别需要说明 define 和 endef 本身不是命令
所以前面一定是不能用 Tab 键的
可以写空格
但是一定是不能用 Tab 键的
还有一种是针对于特定目标的变量
我们称为目标变量
它往往类似于我们 C/C++ 代码里面的局部变量
它往往类似于我们 C/C++ 代码里面的局部变量
只在本目标规则链里有效
本目标规则没问题
为了构建本目标还需要其它的规则
那么这就形成了规则链
它就在这个规则链内是有效的
你就像 “target : var_name = value”
这个就是目标变量
-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 编程实践
-第十五讲 网络编程--编程实践提交入口