文章目录
1. 背景
- 一个工程中的源文件不计其数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作makefile带来的好处就是——“自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率
- make是一个命令工具,是一个解释makefile中指令的命令工具,一般来说,大多数的IDE都有这个命令,比如:Delphi的make,Visual C++的nmake,Linux下GNU的make。可见,makefile都成为了一种在工程方面的编译方法
- make是一条命令,makefile是一个文件,两个搭配使用,完成项目自动化构建
2.实例
下面,我们使用一个例子来示范怎么使用make和makefile
这里分别是我们的C语言源文件和makefile的内容,然后我们使用make指令
可以看到,使用make指令之后,就执行了makefile的第一行,也就第一个依赖方法。当执行make clean时,执行了clean的内容。这样就完成了我们所需要的功能
下面,我们来详细分析一下上面范例中的各个代码的作用
3.原理
首先明确一点,make/makefile的存在是为了构建项目,构建项目需要依赖关系和依赖方法。举个例子,学生时代的我们都会问父母要钱,要钱的时候,我们会说“老爸,我没钱了,给我打钱”,这里前面半句话就是表明了依赖关系:我是你儿子,你是我爸爸,后面半句话就是表明了依赖方法:给我打钱。
所以,在makefile中需要写的就是依赖关系和依赖方法,然后使用make去通过依赖关系执行依赖方法,从而产生我们需要的文件或者指令。
make是如何工作的?
- make会在当前目录下找名字叫“Makefile”或“makefile”的文件。
- 如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到“test_make”这个文件,并把这个文件作为最终的目标文件。
- 如果test_make文件不存在,或是test_make所依赖的后面的test_make.c文件的文件修改时间要比hello这个文件新(可以用 touch 测试),那么,他就会执行后面所定义的命令来生成hello这个文件。
- 如果test_make所依赖的test_make.c文件不存在,那么make会在当前文件中找目标为test_make.c文件的依赖性,如果找到则再根据那一个规则生成test_make.c文件。(这有点像一个堆栈的过程)
- 当然,你的.c文件和.h文件是存在的啦,于是make会生成 test_make.c 文件,然后再用 test_make.c 文件声明make的终极任务,也就是执行文件test_make了。
- 这就是整个make的依赖性,make会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件。
- 在找寻的过程中,如果出现错误,比如最后被依赖的文件找不到,那么make就会直接退出,并报错,而对于所定义的命令的错误,或是编译不成功,make根本不理。
- make只管文件的依赖性,即,如果在我找了依赖关系之后,冒号后面的文件还是不在,那么对不起,我就不工作啦
知道了这些东西,那么我们如何着手去使用make构建项目呢?
makefile的编写
创建一个名为makefile或者Makefile的文件(只能是这两种可能,否则make识别不出来),在makefile中写入内容
建立依赖关系,谁依赖:于谁,例如上述的实例就是test_make依赖于test_make.c,所以在makefile的首行写
test_make:test_make.c
,意思是test_make是由我们自己写的test_make.c编译得来的新起一行,必须以tab键开头(不能是四个空格),写编译的指令,例如上述的
gcc -o test_make test_make.c
makefile的使用
当我们写好makefile之后,直接使用
make 目标文件名/指令名
,就可以达到使用make和makefile构建项目的目的,但是,我们上述的实例中直接使用了make指令,没有加上刚刚说的目标文件,也能够正常执行,达到效果,为什么呢?这是因为在makefile中被一个目标文件直接或者间接关联,那么他后面所定义的命令能够被自动执行。
4.项目清理
工程是需要被清理的
在上述的makefile中,我们同时设置了clean这个目标指令的,他的目的就是为了清除我们编译好的工程文件。
但是我们回到上述实例中,可以发现,多了一行内容,是我们不理解的东西,那么它是什么呢?
.PHONY:表示被该关键字修饰的对象是一个伪目标,这里我们引入了一个新的概念,叫做伪目标
什么是伪目标?
还是使用之前的那个test_make,我们发现,当该路径下没有目标文件的时候,我们能够正常的执行make指令,但是当我们想再次执行make指令的时候,发现出现了提示,没有办法正常执行make指令。反而,我们在执行make clean的时候,没有这种情况,能够连续执行任意次的make clean。这就是伪目标的作用。伪目标表示该目标是永远被执行的!!!
对于上述的情况,我们只需要再修改一下源文件test_make.c,就会发现make又可以正常执行一次了。这是因为,make会识别我们上一次编译之后,源文件是否已经被修改,如果没有被修改,就不需要再次执行编译操作,(这一点是很重要的,一个中大型的项目,编译所花的时间短则几十分钟,长则几个小时甚至十几个小时)。
那么make是怎么知道源文件有没有被修改的呢?
答案是比较时间,比较源文件和目标文件的内容修改时间
我们知道,是先有源文件,在有目标文件的,所以,我们只需要比较源文件和目标文件的内容修改时间,如果源文件的内容修改时间早于目标文件的修改时间,那就说明源文件在生成目标文件之后,没有被更改,所以就没有再次编译的必要,否则就需要再次编译。
5. 文件属性中的三个时间
上面我们讲到了文件的修改时间,在这里我们详细介绍一下文件的“三个时间”。
使用stat指令,我们可以看到文件的详细信息,其中就包括了文件的创建时间,修改时间与最后一次访问时间
对于modify和change的时间是容易理解的,但是access的时间改变规则有些不一样
因为对一个文件的访问,是很频繁的,如果每一次的访问,都要修改access时间的话,会导致系统的负荷太大,所以新的Linux内核就对access时间的修改规则做了修正,变成了根据一段时间内访问频率再去更新。
6. Linux下第一个小程序——进度条
6.1 前置知识1:缓冲区
对于这个测试,执行之后的结果是在屏幕上正常的打印hello world,然后停留一秒钟之后再提示输入下一个指令,但是我们将printf中的‘\n’去掉,然后再编译,执行得到的可执行,会发现屏幕中没有立刻显示hello world,而是再一秒钟后再显示的,这是因为每一行的内容会先放在缓冲区中,当执行回车换行也就是‘\n’之后,才会显示,否则就需要等当前进程结束之后再显示,所以为了让上述程序在没有‘\n’也能正常执行,我们需要使用fflush刷新缓冲区,即:
然后我们发现该程序可以正常执行输出然后再停顿了。
6.2前置知识2:回车换行
在刚刚,我们提到了回车换行的概念,但是,回车和换行是两个概念,回车表示的是回到当前行的开头位置,用\r表示,换行表示光标换到下一行的同一位置,但是到这里就会有疑问,我们键盘上的enter键,一般不就是叫做回车键嘛?我们注意一下早期的键盘上,回车键是什么样子的
用了一个向下和向左的箭头表示着这个键的作用是让光标去到下一行的开头。
那么如果不换行,只回车的话,会出现什么情况呢?
答案是会将屏幕上当前行的内容覆盖掉。
那么,利用这种特性,我们能够写出一个简易的倒计时程序
运行的结果是这样的:
这就是我们利用缓冲区和回车换行的特性写出来的简易倒计时。
6.3进度条的实现
有了上述的两个前置知识,我们就可以利用已知的知识写出进度条啦
接下来我们尝试一下多文件编程,首先定义一下头文件和主要的实现源文件,还有主函数文件,分别命名为process.h,process.c,main.c,然后对于一整个项目,我们要写一下makefile文件所以一共要创建四个文件
$ touch makefile process.c process.h main.c
各个部分代码如上,然后使用make编译,产生可执行文件ProcessOn,运行效果如下
7 Linux下git的”三板斧“
由于Linux是没有图形化界面的,所以我们在Windows下使用的“小乌龟”在这里就不能用了,所以我们要学一下git的命令行使用方式
1. git clone
首先,我们需要将已有的仓库拷贝到本地,所以就有了git clone指令,该指令后面跟我们需要拷贝的仓库的链接
2. git add
我们在本地写的代码需要添加到本地仓库,所以需要使用git add指令添加,git add指令后跟文件名,这里我们经常使用的是
git add .
表示将所有新增文件全部添加3. git commit
对标Windows下使用小乌龟的过程,我们同样需要commit,我们在commit的同时,还需要附带本次上传的说明,
git commit -m "说明文字"
4. git push
上传到本地仓库之后,我们还需要上传到线上的仓库,对标”小乌龟“,我们需要push,所以使用git push指令
注:默认每次上传的时候都要输入用户名和密码,这里可以配置成不需要输入,但是还是建议每次都手动输入点我配置免密码提交
5. git log
我们的每次上传都会被记录下来,成为日志,使用git log就可以查看提交日志
本节完。。。