目录
五、Linux项目自动化构建工具 - make/Makefile
5.2 make 命令和 makefile 文件、理解依赖关系和依赖方法
一、开发工具
假设有人问你在什么环境下写代码 ?
你的回答:vs2019
那人又问你在什么环境下调试代码?
你的回答:vs2019
那人又再次问你在什么环境下编译、链接代码?
你的回答:vs2019
这里所回答的 vs2019 是一个集成开发环境,也就是我们所说的 IDE。我们学习 C/C++ 之前都使用的的开发环境都是集成开发环境,就是一些常用的编译器,如 vs、vscode等等
集成开发的编译器集成了代码的编写功能、分析功能、编译功能、调试功能等一体化功能。而在Linux 上的环境不是集成开发环境,它们都是独立的,比如编写代码一般使用 vim,编译代码一般使 gcc(编译C语言)、g++(编译C/C++)等,调试代码一般使用 GDB 等,维护项目关系用 make/Makefile 等
二、Linux编辑器 - vim使用
vi/vim 的区别简单点来说,它们都是多模式编辑器,不同的是 vim 是 vi 的升级版本,它不仅兼容 vi 的所有指令,而且还有一些新的特性在里面。例如语法加亮,可视化操作不仅可以在终端运行,也可以运行于x window、 mac os、windows。这里介绍 vim 编辑器的使用
2.1 vim 的基本概念
vim 有十二种模式之多,这里就讲解 vim 的三种模式(目前掌握这3种即可),分别是命令模式(command mode)、插
入模式(Insert mode)和底行模式(last line mode),各模式的功能区分如下:
(1)正常/普通/命令模式(Normal mode)
- 控制屏幕光标的移动,字符、字或行的删除,移动复制某区段及进入Insert mode下,或者到 last line mode
(2)插入模式(Insert mode)
- 只有在Insert mode下,才可以做文字输入,按「ESC」键可回到命令行模式。该模式是我们后面用的最频繁的编辑模式。
(3)末行模式(last line mode)
- 文件保存或退出,也可以进行文件替换,找字符串,列出行号等操作。 在命令模式下,shift+: 即可进入该模式。要查看你的所有模式:打开vim,底行模式直接输入:help vim-modes
2.2 vim的基本操作
输入 vim,进入 vim,进入之后画面相同或类似则说明 vim 正常,可以直接使用
vim
(1)进入 vim,在系统提示符号输入vim及文件名称后,就进入vim全屏幕编辑画面
[fy@VM-4-14-centos code_98]$ vim [文件名]
- 不过有一点要特别注意,就是你进入vim之后,是处于【正常模式】,你要切换到【插入模式]】才能够输入文字。后面跟的文件假如不存在则直接创建
(2)【正常模式】切换至【插入模式】
输入a 或输入i 或输入o
(3)【插入模式】切换至【正常模式】
- 目前处于[插入模式],就只能一直输入文字,如果想切换回【正常模式】可以先按一下「ESC」键转到【正常/命令模式】
(4)【正常模式】切换至【末行模式】
- 「shift + ;」, 其实就是输入「:」,换成【末行模式】
(5)退出 vim 及保存文件,在【命令模式】下,按【:】进入【末行模式】,再输入以下选项
- :w (保存当前文件)
- :wq (存盘并退出 vim)
- : q! (不存盘强制退出 vim)
- :wq!(强制保存并退出)
注意插入模式不能转换成底模式,底行模式转插入模式也是如此,模式切换图如下:
2.3 vim正常模式命令集
(1)插入模式
- 按「i」切换进入插入模式「insert mode」,按“i”进入插入模式后是从光标当前位置开始输入文件;
- 按「a」进入插入模式后,是从目前光标所在位置的下一个位置开始输入文字;
- 按「o」进入插入模式后,是插入新的一行,从行首开始输入文字
(2) 从插入模式切换为命令模式
- 按「ESC」键
(3)移动光标
- vim 可以直接用键盘上的光标来上下左右移动,但正规的vim是用小写英文字母「h」、「j」、「k」、「l」,分别控制光标左、下、上、右移一格
- 按「 $ 」:移动到光标所在行的“行尾”
- 按「^」:移动到光标所在行的“行首”
- 按「w」:光标跳到下个字的开头
- 按「e」:光标跳到下个字的字尾
- 按「b」:光标回到上个字的开头
- 按「nl」:光标移到该行的第n个位置,n为具体的数字,如:5l,56l
- 按[gg]:进入到文本开始
- 按[【shift+g】 = G]:进入文本末端
- 按「ctrl」+「b」:屏幕往“后”移动一页
- 按「ctrl」+「f」:屏幕往“前”移动一页
- 按「ctrl」+「u」:屏幕往“后”移动半页
- 按「ctrl」+「d」:屏幕往“前”移动半页
(4)删除文字
- 「x」:每按一次,删除光标所在位置的一个字符
- 「nx」:例如,「6x」表示删除光标所在位置的“后面(包含自己在内)”6个字符,n为具体的数字
- 「X」:大写的X,每按一次,删除光标所在位置的“前面”一个字符
- 「nX」:例如,「20X」表示删除光标所在位置的“前面”20个字符,n为具体的数字
- 「dd」:删除光标所在行
- 「#dd」:从光标所在行开始删除#行
(5)复制
- 「yw」:将光标所在之处到字尾的字符复制到缓冲区中。
- 「nyw」:复制n个字到缓冲区,n为具体的数字
- 「yy」:复制光标所在行到缓冲区。
- 「nyy」:例如,「6yy」表示拷贝从光标所在的该行“往下数”6行文字,n为具体的数字
- 「p」:将缓冲区内的字符贴到光标所在位置。注意:所有与“y”有关的复制命令都必须与“p”配合才能完成复制与粘贴功能。
(6)撤销上一次操作
- 「u」:如果您误执行一个命令,可以马上按下「u」,回到上一个操作。按多次“u”可以执行多次回复。
- 「ctrl + r」: 撤销的恢复
(7)替换
- 「r」:替换光标所在处的字符。
- 「R」:替换光标所到之处的字符,直到按下「ESC」键为止。
(8)更改
- 「cw」:更改光标所在处的字到字尾处
- 「c#w」:例如,「c3w」表示更改3个字,#为具体数字
(9)跳至指定的行
- 「ctrl」+「g」列出光标所在行的行号。
- 「#G」:例如,「15G」,表示移动光标至文章的第15行行首,#为具体数字
(10)切换大小写
- 【shift + ~】:切换大小写
2.4 vim末行模式命令集
在使用末行模式之前,请记住先按「ESC」键确定您已经处于正常模式,再按「:」冒号即可进入末行模式。
(1)列出行号
- 「set nu」: 输入「set nu」后,会在文件中的每一行前面列出行号
- 「set nonu」: 输入「set nonu」后,文件中的每一行前面行号会取消
(2) 跳到文件中的某一行
- 「#」:「#」号表示一个数字,在冒号后输入一个数字,再按回车键就会跳到该行了,如输入数字15,再回车,就会跳到文章的第15行
(3) 查找字符
- 「/关键字」: 先按「/」键,再输入您想寻找的字符,如果第一次找的关键字不是您想要的,可以一直按「n」会往后寻找到您要的关键字为止。
- 「?关键字」:先按「?」键,再输入您想寻找的字符,如果第一次找的关键字不是您想要的,可以一直按「n」会往前寻找到您要的关键字为止
- / 是从文本的开头开始往下查找,? 是从文本结尾开始往上查找
(4) 保存文件
- 「w」: 在冒号输入字母「w」就可以将文件保存起来
(5) 离开vim
- 「q」:按「q」就是退出,如果无法离开vim,可以在「q」后跟一个「!」强制离开 vim
- 「wq」:一般建议离开时,搭配「w」一起使用,这样在退出的时候还可以保存文件
(6) 分屏
- [vs 文件名]:可以在当前窗口下新增一个窗口进行分屏
- 【ctrl + ww】可以将光标定位到另一个窗口,注意退出 vim 的窗口是光标所在的窗口
(7) 不退出vim 执行Linux 命令
- 底行模式下 ![命令],不退出 vim 执行相应的命令(执行命令行,编译,运行,查看 man 等等)
2.5 简单vim配置
没有进行配置的 vim对我们非常不友好,写代码也不方便。所有我们需要对 vim 进行配置
2.5.1 配置文件的位置
- root 用户在目录 /etc/ 下面,有个名为 vimrc 的文件,这是系统中公共的vim配置文件,对所有用户都有效。
- 而在每个普通用户的主目录下,都可以自己建立私有的配置文件,命名为:“.vimrc”。例如,/root目录下,通常已经存在一个 .vimrc 文件,如果不存在,则创建之,普通用户在家目录下也是如此。
- 切换用户成为自己执行 su ,进入自己的主工作目录,执行 cd ~
- 打开自己目录下的 .vimrc文件,执行 vim .vimrc,没有则创建 .vimrc文件
2.5.2 常用配置选项,用来测试
- 设置语法高亮: syntax on
- 显示行号: set nu
- 设置缩进的空格数为4: set shiftwidth=4
- 网上搜索可有更多 vim的配置选项,这里只列出几个
这里的vim 是自己配置的,(有耐心的可以自己慢慢配置)ps:
2.5.3 使用插件(新学者不推荐)
要配置好看的vim,原生的配置可能功能不全,可以选择安装插件来完善配置,保证用户是你要配置的用户,接下来:
- 安装TagList插件,下载 taglist_xx.zip ,解压完成,将解压出来的doc的内容放到~/.vim/doc, 将解压出来的plugin下的内容拷贝到~/.vim/plugin
- 在~/.vimrc 中添加: let Tlist_Show_One_File=1 let Tlist_Exit_OnlyWindow=1 letTlist_Use_Right_Window=1
- 安装文件浏览器和窗口管理器插件: WinManager
- 下载winmanager.zip,2.X版本以上的
- 解压winmanager.zip,将解压出来的doc的内容放到~/.vim/doc, 将解压出来的plugin下的内容拷贝到~/.vim/plugin
- 在~/.vimrc 中添加 let g:winManagerWindowLayout=‘FileExplorer|TagList nmap wm :WMToggle<cr>
- 然后重启vim,打开~/XXX.c或~/XXX.cpp, 在normal状态下输入"wm", 你将看到上图的效果。
- 更具体移步:点我, 其他手册,请执行 vimtutor 命令
是不是挺麻烦的,接下来还有一种比较简便的方法。
2.5.4 gitee(推荐)
支持环境:目前只支持 Centos7 x86_64
项目简介:
本项目主要目标是帮助对vim配置方法不熟悉的新手封装的一键式vim环境安装包. 主要针对终端 vim用户, 适合远程ssh连接 Linux服务器进行开发的场景(例如使用阿里云服务器或者腾讯云服务器等)
特点:
- 安装速度快(使用码云而不是github作为源). 网络畅通情况下, 几分钟内完成 vim 插件安装.
- 无需编译直接使用 YouCompleteMe(直接下载预编译好的 ycm_core.so).
- 一键式安装. 真正做到一键式安装. 不光能一键式安装 Vim 配置, 同时也会安装依赖的程序(包括 git, neovim, ctags等)
详细安装步骤请点击这里 ,该作者对 vim 有详细介绍。
2.5.5 vim配置参考资料(github)
三、关于 sudo 提升权限问题
这是在 Linux权限 中遗留的问题 ,曾经我们在普通用户的操作中,想使用 sudo 来短期提升权限,却并不能如愿,这是因为默认这个用户是不受信任的。先来解释 sudo
3.1 sudo 简介
sudo 是 Linux 系统管理指令,是允许系统管理员让普通用户执行一些或者全部的 root 命令的一个工具,如halt,reboot,su等等。这样不仅减少了 root 用户的登录 和管理时间,同样也提高了安全性。sudo 不是对 shell 的一个代替,它是面向每个命令的。
- sudo 的作用:提升普通用户的权限,普通用户就可以干一些超级用户才能干的事,该权限有使用时间限制,时间到了再次输入自己的用户密码就可以继续使用 sudo,sudo 默认存活期为5分钟。
3.2 赋予普通用户 sudo 权限
(1)先 su - 切换成 root,在 root 下操作
(2)vim /etc/sudoers 打开配置文件,进去之后是这样子的
(3)给文本加上行号,set nu
(5)大概在100行左右,命令模式输入 100G,则跳转到100行,图中圈起来的则是要改的
(6)这时候复制下面这个,在它的下一行进行粘贴,然后修改前面的 %wheel 即可,把它改成自己的用户名字就可以了,其他的不用改。
(7)改完之后切换成命令模式保存并退出就可以了,注意这里退出并保存需要 wq! ,保存退出后大功构成,赋予权限的普通用户就可以使用 sudo 命令了
四、Linux编译器 - gcc/g++使用
4.1 背景知识
在学习 C语言的时候,已经学习过了。C语言学的时候大多数认识理论,现在要将学习的理论进行实践。
vim 是用来编辑代码,gcc/g++ 则是用来编译代码,gcc用来编译 C语言,g++既可编译C++,也可编译 C语言。
- 预处理(去注释,进行宏替换,头文件展开,条件编译...)
- 编译(生成汇编)
- 汇编(生成机器可识别代码,也叫可重定向二进制目标文件)
- 链接(链接多个 .o/.obj 文件,生成可执行文件或库文件)
在此之前,我们可以使用命令 gcc -v 、g++ -v 查看 gcc、g++ 有没有安装:
gcc -v
g++ -v
显示有相关信息则安装好了,ps:
如果显示没有安装,输入下面这条指令即可安装:
sudo yum install -y gcc-c++
4.2 gcc如何完成上述工作?
格式: gcc [选项] 要编译的文件 [选项] [目标文件]
gcc选项:
- -E 只激活预处理,这个不生成文件,你需要把它重定向到一个输出文件里面
- -S 编译到汇编语言不进行汇编和链接
- -c 编译到目标代码
- -o 文件输出到 文件
- -static 此选项对生成的文件采用静态链接
- -g 生成调试信息。GNU 调试器可利用该信息。
- -shared 此选项将尽量使用动态库,所以生成文件比较小,但是需要系统由动态库.
- O0
- O1
- O2
- O3 编译器的优化选项的4个级别,-O0表示没有优化,-O1为缺省值,-O3优化级别最高
- -w 不生成任何警告信息。
- -Wall 生成所有警告信息。
如何生成可执行程序?
gcc/g++ [要编译的文件]
生成的 a.out 就是可执行程序,可以直接运行。那问题又来了,怎么执行呢?
1、表面当前路径 2、表明可执行程序的名字,ps:. /表示当前路径下,a.out 表示要执行的程序。
./a.out
4.2.1 预处理
- 预处理功能主要包括去注释,进行宏替换,头文件展开,条件编译等。
- 预处理指令是以#号开头的代码行。
- 实例: gcc –E test.c –o test.i
- 选项“-E”,该选项的作用是让 gcc 在预处理结束后停止编译过程。
- 选项“-o”是指目标文件,“.i”文件为已经过预处理的C原始程序
test.c
#include<stdio.h>
#define N 100
int main()
{
printf("hello world1\n");
printf("hello world2\n");
printf("hello world3\n");
//printf("hello world4\n");
//printf("hello world5\n");
printf("hello world6\n");
printf("%d\n", N);
return 0;
}
gcc –E test.c -o test.i ,-E”,该选项的作用是让 gcc 在预处理结束后停止编译过程,“-o”是指生成的目标文件,“.i”文件为已经过预处理的C原始程序
gcc –E test.c -o test.i
打开 test.i,下面宏定义后面多了个分号,无视即可
按[shift g] /G 翻到末尾
与 test.c 进行对比,宏确实进行了替换,注释确实去掉了,头文件确实展开(前面的八百多行)
4.2.2 编译(生成汇编)
- 在这个阶段中,gcc 首先要检查代码的规范性、是否有语法错误等,以确定代码的实际要做的工作,在检查无误后,gcc 把代码翻译成汇编语言。
- 用户可以使用“-S”选项来进行查看,该选项只进行编译而不进行汇编,生成汇编代码。
- 实例: gcc –S test.i –o test.s
gcc –S test.i –o test.s,“-S”,该选项只进行编译而不进行汇编,生成汇编代码,编译完成就停下来,“-o”是指生成的目标文件,“.s”文件为已经过编译生成了汇编代码
ps:
4.2.3 汇编(生成机器可识别代码)
- 汇编阶段是把编译阶段生成的“.s”文件转成目标文件 .o
- 读者在此可使用选项“-c”就可看到汇编代码已转化为“.o”的二进制目标代码了
- 实例: gcc –c test.s –o test.o
gcc –c test.s –o test.o ,“-c”,该选项把汇编代码转换成二进制代码,汇编完成就停下来,“-o”是指生成的目标文件,“.o”文件为已经过汇编转换成了机器可识别代码,也就是二进制代码,该 .o文件的全称是可重定向目标文件。在Windows 下生成的是 .obj文件。
ps:
4.2.4 链接(生成可执行文件或库文件)
- 在成功编译之后,就进入了链接阶段,链接完成就生成了可执行文件(在Windows下是以 .exe 结尾的)或库文件
- 实例: gcc test.o –o mytest
进行运行:
4.2.5 gcc选项记忆
为了方便记忆,我们可以看看键盘的左上角的 Esc 键,它刚好对应我们的 -E、-S、-c 选项;而生成的文件的后缀刚好对应镜象文件的后缀 .iso
4.3 静态库和动态库
在进行链接的时候涉及到一个重要的概念:函数库
- 在 C程序中,并没有定义“printf”的函数实现,且在预编译中包含的“stdio.h”中也只有该函数的声明,而没有定义函数的实现,那么,是在哪里实“printf”函数的呢?
- 最后的答案是:系统把这些函数实现都被做到名为 libc.so.6 的库文件中去了,在没有特别指定时,gcc 会到系统默认的搜索路径“/usr/lib”下进行查找(在Linux下),也就是链接到 libc.so.6 库函数中去,这样就能实现函数“printf”了,而这也就是链接的作用
函数库一般分为静态库和动态库两种
ldd 命令查看可执行程序所依赖的库,格式 ldd [可执行程序],ps:
file 命令则辨别 可执行程序的类型,是静态库还是动态库。格式:file [可执行程序],ps:
4.3.1 静态库
静态库是指编译链接时,把库文件的代码全部加入到可执行文件中,因此生成的文件比较大,但在运行时也就不再需要库文件了。在 Linux 其后缀名一般为 “.a”,在Windows下其后缀一般为 “.lib”
4.3.2 动态库
动态库与之相反,在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时由运行时链接文件加载库,这样可以节省系统的开销。
在 Linux下动态库一般后缀名为“.so”,如前面图中的 libc.so.6 就是动态库;在Windows下其后缀一般为 “.dll”。gcc/g++ 在编译时默认使用动态库。完成了链接之后,gcc/g++ 就可以生成可执行文件,如: gcc test.o –o mytest 生成的 mytest。
小故事帮助理解:
- 比如小明是一个新入学的大一新生,因为家里比较穷,没有钱买电脑。小明因为有上网的需求,因为环境不熟悉,所以问了学长附近的网吧,随后就按照学长所说路线找到了网吧,小明上网上够了就回学校了。小明没有电脑只能依赖网吧的电脑上网,这叫依赖关系,学长所提供的去网吧的路径,依赖这条路径找到网吧,这叫依赖方法,这就叫动态链接。动态链接就是将库中我需要的方法的地址填入我的可执行程序中,与外界建立关联,我需要的时候就顺着库中的方法的地址找到我所需要的,这样的动态链接可以节省资源。
- 小明到大二了,经过打工赚够了买电脑的钱,小明高高兴兴的买了一台电脑。当小明想上网的时候就打开他的电脑,不用去网吧了。小明为什么不去网吧了?因为他买了一台电脑,这叫依赖方法。小明在宿舍依赖他自己的电脑上网,这叫依赖关系。静态链接就是将库中的方法实现,真的拷贝到我们的可执行程序中,没有与外界建立关联,这种静态链接比较占用资源。
- gcc/g++ 默认形成的可执行程序是动态链接的
动态链接所占用空间大小(同一代码):
静态链接所占用空间大小(同一代码),如何使用静态库?
gcc test.c -o mytest-s -static 此选项对生成的文件采用静态链接
注意:使用 -static 首先要安装静态库,因为Linux下没自带有,输入如下指令进行安装,输入用户密码即可进行安装
//C语言静态库安装 sudo yum install -y glibc-static //C++静态库安装 sudo yum install -y libstdc++-static
有如下提示即安装成功:
接下来 gcc test.c -o mytest-s -static 此选项对生成的文件采用静态链接,执行后生成的文件明显比动态链接所生成的文件大得多
- 动态链接的生成的可执行程序的体积往往比较小,但是它依赖第三方库,有一定的风险,万一找不到第三方库,程序就无法运行。
- 静态链接虽然生成的可执行程序的体积较大,但是它不依赖第三方库,即使没有第三方的库,程序照样也能运行
- 总的来说,两者既有优点,也有缺点,看在哪种场景下使用
五、Linux项目自动化构建工具 - make/Makefile
以前我们写一些 C/C++小项目的时候,项目都是编译器帮我们维护的,在Linux下则需要我们自己维护,维护的工具就是 make/makefile
5.1 背景
5.1.1 多个源文件带来的问题
- 在编写c/c++测试程序时,我们习惯每次修改一处代码,然后就马上编译运行来查看运行的结果。这种编译方式对于小程序来说是没有多大问题的,可对于大型程序来说,由于包含了大量的源文件,如果每次改动一个地方都需要编译所有的源文件,这个简单的直接编译所有源文件方式对程序员来说简直是噩耗
看一个例子:
// 1.c 包含的头文件 #include "1.h" // 2.c 包含的头文件 #include "1.h" #include "2.h" // 3.c 包含的头文件 #include "2.h" #include "3.h"
- 如果只修改了头文件 3.h,则源文件 1.c 和 2.c 都无需编译,因为它们不依赖这个头文件。而对 3.c 来说,由于它包含了3.h,所以在头文件 3.h 改动后,就必须得新编译,因为3.c 依赖于 3.h
- 而如果改动了 2.h 可是忘记重新编译 2.c,那么最终的程序就可能无法正常工作。
- make/makefile 工具就是为了解决上述问题而出现的,它会在必要时重新编译所有受改动影响的源文件
5.1.2 另一方面
- 会不会写makefile,从一个侧面说明了一个人是否具备完成大型工程的能力
- 一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作
- makefile带来的好处就是——“自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率。
- make是一个命令工具,是一个解释makefile中指令的命令工具,一般来说,大多数的IDE都有这个命令,比如:Delphi的make,Visual C++的nmake,Linux下GNU的make。可见,makefile都成为了一种在工程方面的编译方法。
- make是一条命令,makefile是一个文件,两个搭配使用,完成项目自动化构建
5.2 make 命令和 makefile 文件、理解依赖关系和依赖方法
首先明确一点:
make 是一条命令,它可以帮我们自动化构建项目
makefile 是一个文件,自动化构建项目的过程是它完成的,makefile 的 m 既可大写也可小写。
make命令功能虽然十分强大,但是光凭其自身无法构建应用程序的。这时,makefile就出来了,它告诉make应用程序如何构建的。make 命令和 makefile 文件的结合提供了一个在管理项目的十分强大的工具,它们不仅用于控制源文件的编译,而且还提供了将应用程序安装到目标目录等其他功能。
用以下代码测试,只有 test.c 一个源文件
#include<stdio.h> int main() { printf("hello world\n"); return 0; }
接下来在该路径下 vim makefile
先写要生成目标的名称,然后紧跟一个冒号,接着紧跟源文件名字,接着是换行,换行之后回退到顶格,再按下 tab 键,不能敲四个空格,按tab键后紧跟需要编译的文件,最后保存退出。
mytest:test.c gcc test.c -o mytest
接下来直接 make 就可以了
最后执行程序
到这里暂停一下,依赖关系和依赖方法上面也讲到,陈述一下这里的依赖关系和依赖方法。
- 依赖关系定义了应用程序里面每个文件与其他源文件之间的关系。我们都知道 test.c 生成 mytest,这里的生成关系是正向的,反过来 mytest 是依赖于 test.c,这就是这里的依赖关系。
- 而依赖方法则表明是谁生成的可执行程序,依赖这种方法而生成可执行程序。上面的 test.c 就是依赖于 gcc 而生成的 mytest,这里的 gcc 就是依赖方法。
- 在 makefile 里面,依赖关系和依赖方法总是成对出现的。
- make 默认执行第一对关系方法,如果有多对要执行其中一对,则 make +要执行的名称即可,因为make默认从上到下执行Makefile里面的关系方法对
5.3 原理
make是如何工作的,在默认的方式下,也就是我们只输入make命令。那么
- make 会在当前目录下找名字叫 “Makefile” 或 “makefile” 的文件。
- 如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到“test.c”这个文件,并把这个文件作为最终的目标文件。
- 如果 "mytest" 文件不存在,或是 mytest 所依赖的后面的 test.c文件的文件修改时间要比 mytest 这个文件新(可以用 touch 测试),那么,他就会执行后面所定义的命令来生成 mytest 这个文件。
- 如果 mytest 所依赖的 test.c 文件不存在,那么make会在当前文件中找目标为 test.c 文件的依赖性,如果找到则再根据那一个规则生成 test.c(这里的test .c 是我自己写的,想一下上面的预处理,编译,汇编,链接生成的文件就懂了) 文件。(这有点像一个堆栈的过程)
- 当然,你的C文件和H文件是存在的啦,于是make会生成 test.c (这里的test .c 是我自己写的,想一下上面的预处理,编译,汇编,链接生成的文件就懂了)文件,然后再用 test.c 文件声明make的终极任务,也就是执行文件 mytest 了。
- 这就是整个make的依赖性,make会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件。
- 在找寻的过程中,如果出现错误,比如最后被依赖的文件找不到,那么make就会直接退出,并报错,而对于所定义的命令的错误,或是编译不成功,make根本不理。
- make只管文件的依赖性,即,如果在我找了依赖关系之后,冒号后面的文件还是不在,那么对不起,我就不工作啦
5.3 项目清理
一个项目不仅需要能生成文件,还要能清理文件。那如何清理?
首先,打开 makefile 文件,在紧接着下一行输入如下(按tab键取空格,不要打空格),然后保存退出
.PHONY:clean clean: rm -f mytest
clean 的依赖文件(依赖关系)可以为空,rm -f mytest 为依赖方法
输入 make clean,即可清理
- 工程是需要被清理的
- 像clean这种,没有被第一个目标文件直接或间接关联,那么它后面所定义的命令将不会被自动执行,不过,我们可以显示要make执行。即命令——“make clean”,以此来清除所有的目标文件,以便重编译。
- 但是一般我们这种clean的目标文件,我们将它设置为伪目标,用 .PHONY 修饰,伪目标的特性是,总是被执行的。
- 可以将我们的 mytest目标文件声明成伪目标,测试一下
5.3.1 解释 .PHONY
.PHONY 称为伪目标,它总是被执行的。
怎么理解呢?
我们在生成 mytest 之后继续make,进行生成:
这是为什么呢?这是因为生成的文件已经是最新的版本了,直接提示你不用再更新了,已经是最新的版本了。
那再如,一直执行 make clean 呢?结果如下:
结果说明它总能被执行,不管文件有没有。这是为什么呢么?没错,就是因为 .PHONY 伪目标的原因,这下能理解伪目标总被执行了吧。如果给 mytest 添加伪目标,它也会一直会被执行,不管你是不是最新版。
这里是习惯上给 clean 添加上伪目标的
5.3.2 makefile 如何得知可执行程序是最新的?
答案是根据文件的最近修改时间来的
接下来要了解什么是 AMC时间?
输入 stat [文件] 查看 AMC时间
Access:文件最近访问时间
- 对于文件来说,当我们用编辑器打开file,或使用cat more less grep sed 等等命令读取文件内容,以及使用file cp命令操作文件,或执行可执行文件时,Access时间会被更新,空文件也不例外。
- 对于目录来说,只进入目录不会更新其Access时间,但是通过ls查看目录内容时,Access时间就会更新。使用ls -lu查看文件时,会显示出来文件的Access时间。
Modify:文件的内容最后一次被修改的时间
- 当更改一个文件的内容时,此文件的modify时间记录会被更新。Modify时间更新时,Access和Change时间都会得到相应的更新。
Change:改变时间
- 主要是指文件的状态或属性的改变。对一个文件或目录进行mv chown chcgrp等操作后,change 时间会更新
这里暂时没有进行测试
5.5 多文件项目的构建
这里我已经建好三个文件了,ps:
vim makefile 进行编写:
mytest:test.o main.o
gcc -o mytest test.o main.o
test.o:test.c
gcc -c test.c -o test.o
main.o:main.c
gcc -c main.c -o main.o
.PHONY:clean
clean:
rm -f *.o mytest
make 执行
删除项目
这里的多项目操作可以看上面的原理,帮助理解
文章先到这里,下篇即将更新