Linux编译gcc/g++、自动化构建工具make/makefile

您所在的位置:网站首页 stripped翻译 Linux编译gcc/g++、自动化构建工具make/makefile

Linux编译gcc/g++、自动化构建工具make/makefile

2023-04-03 10:32| 来源: 网络整理| 查看: 265

目录

1.g++/gcc的使用

2.Makefile

1.g++/gcc的使用

在学习gcc/g++之前,需要先回顾一下程序的翻译过程:

预处理(头文件展开、去注释、宏替换、条件编译) 编译:把c编程汇编语言 汇编:把汇编变成二进制(不是可执行,二进制目标文件) 链接:把写的代码和c标准库中的代码合起来

gcc的格式:gcc [选项] 要编译的文件 [选项] [目标文件]

[wjmhlh@VM-12-9-centos lesson7] gcc test.c [wjmhlh@VM-12-9-centos lesson7] ll total 16 -rwxrwxr-x 1 wjmhlh wjmhlh 8360 Nov  9 20:27 a.out -rw-rw-r-- 1 wjmhlh wjmhlh  320 Nov  9 20:27 test.c [wjmhlh@VM-12-9-centos lesson7]

直接使用语句:gcc test.c 会直接编译完成,不会显示程序的翻译的过程。

因此,以下代码的含义:

预处理: gcc -E test.c -o test.i -o:指明形成的临时文件名称,.i(后缀,在linux中后缀没意义,是给人看的) ;-E:从现在开始,进行程序的翻译,当我将预处理做完,就停下来。 [wjmhlh@VM-12-9-centos lesson7] gcc -E test.c -o test.i [wjmhlh@VM-12-9-centos lesson7] ll total 36 -rwxrwxr-x 1 wjmhlh wjmhlh  8360 Nov  9 20:27 a.out -rw-rw-r-- 1 wjmhlh wjmhlh   320 Nov  9 20:27 test.c -rw-rw-r-- 1 wjmhlh wjmhlh 17005 Nov  9 20:30 test.i  

可以使用vim test.i 打开查看:可以看到,头文件展开后的预处理的代码。并且,注释部分没有了,在条件编译下的printf("hello show\n");也消失了,因为SHOW这个宏没定义,所以只保留了default那一行代码,这就是条件编译;

[wjmhlh@VM-12-9-centos lesson7]$ ./a.out hello1  hello2  hello3  hello deafult 宏: 234

   如果需要条件编译生效,需要定义宏

以下是结果: 

hello1  hello2  hello3  hell show 宏: 234

同时:我们也可以在外面定义宏:在文件名后加-D+宏名

[wjmhlh@VM-12-9-centos lesson7] gcc test.c -DSHOW [wjmhlh@VM-12-9-centos lesson7] ./a.out hello1  hello2  hello3 hell show 宏: 234 [wjmhlh@VM-12-9-centos lesson7] gcc test.c [wjmhlh@VM-12-9-centos lesson7] ./a.out hello1  hello2  hello3 hello deafult 宏: 234

当预处理完后,代码还是C语言,只不过是“干净”的C语言,没有注释没有未定义。

于是,接下来,要将C语言变成汇编语言。

编译 gcc -S test.c -o test.s  -S:从现在开始,进行程序的翻译,做完编译工作,变成汇编之后,就停下来

[wjmhlh@VM-12-9-centos lesson7] gcc -S test.c -o test.o [wjmhlh@VM-12-9-centos lesson7] ll total 40 -rw-rw-r-- 1 wjmhlh wjmhlh   321 Nov  9 20:47 test.c -rw-rw-r-- 1 wjmhlh wjmhlh 16988 Nov  9 20:45 test.i -rw-rw-r-- 1 wjmhlh wjmhlh   710 Nov  9 20:55 test.s [wjmhlh@VM-12-9-centos lesson7]

下面这个图的test.o,写错了,应该是test.s

 此时,已经将C语言,变成汇编语言了,但是,汇编语言并不能被计算机执行,而是二进制;

于是,需要进行汇编,汇编就是将汇编语言,变成二进制(不可执行,二进制目标文件)。

汇编 gcc -c test.s -o test.o -c:从现在开始,进行程序的翻译,做完汇编工作,编程可重定向目标二进制,就停下来

[wjmhlh@VM-12-9-centos lesson7] gcc -c test.c -o test.o [wjmhlh@VM-12-9-centos lesson7] ll total 44 -rw-rw-r-- 1 wjmhlh wjmhlh   321 Nov  9 20:47 test.c -rw-rw-r-- 1 wjmhlh wjmhlh 16988 Nov  9 20:45 test.i -rw-rw-r-- 1 wjmhlh wjmhlh  1808 Nov  9 21:04 test.o -rw-rw-r-- 1 wjmhlh wjmhlh   710 Nov  9 21:01 test.s [wjmhlh@VM-12-9-centos lesson7]

 虽然变成了二进制,但是还是不能被执行,因为少了链接,没有将库结合起来。因为这三部,都只在编译我的代码,并没有带上C标准库上的代码。

链接 gcc test.o -o mytest  链接过程,形成了可执行程序,可执行的二进制程序(库+我写码)。

[wjmhlh@VM-12-9-centos lesson7] gcc test.o [wjmhlh@VM-12-9-centos lesson7] ll total 44 -rwxrwxr-x 1 wjmhlh wjmhlh  8408 Nov  9 21:08 a.out -rw-rw-r-- 1 wjmhlh wjmhlh   321 Nov  9 20:47 test.c -rw-rw-r-- 1 wjmhlh wjmhlh 16988 Nov  9 20:45 test.i -rw-rw-r-- 1 wjmhlh wjmhlh  1809 Nov  9 21:04 test.o -rw-rw-r-- 1 wjmhlh wjmhlh   710 Nov  9 21:01 test.s

当然,如果我们想重命名,可以带上-o

[wjmhlh@VM-12-9-centos lesson7] gcc test.o -o mytest [wjmhlh@VM-12-9-centos lesson7] ll total 56 -rwxrwxr-x 1 wjmhlh wjmhlh  8408 Nov  9 21:08 a.out -rwxrwxr-x 1 wjmhlh wjmhlh  8408 Nov  9 21:10 mytest -rw-rw-r-- 1 wjmhlh wjmhlh   321 Nov  9 20:47 test.c -rw-rw-r-- 1 wjmhlh wjmhlh 16988 Nov  9 20:45 test.i -rw-rw-r-- 1 wjmhlh wjmhlh  1809 Nov  9 21:04 test.o -rw-rw-r-- 1 wjmhlh wjmhlh   710 Nov  9 21:01 test.s [wjmhlh@VM-12-9-centos lesson7]

在链接这一步:

在这里涉及到一个重要的点:函数库

我们的C程序中,并没有定义“printf”的函数实现,且在预编译中包含的“stdio.h”中也只有该函数的声明,而没有定义函数的实现,那么,是在哪里实“printf”函数的呢?

接下来,就是函数库的作用了!

函数库一般分为静态库和动态库

静态库是指编译链接时,把库文件的代码全部加入到可执行文件中,因此生成的文件比较大,但在运行时也就不再需要库文件了。

静态链接:好处:不受库升级或被删除的影响。坏处:形成的可执行程序太大。

动态库与之相反,在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时由运行时链接文件加载库,这样可以节省系统的开销,即形成的可执行程序小。

动态链接:好处:形成的可执行程序小。坏处:受库的改动影响。

那么,在一般情况下,我们的Linux的函数库是静态还是动态的呢?

先看下面代码:

[wjmhlh@VM-12-9-centos lesson8] touch mytest.c [wjmhlh@VM-12-9-centos lesson8] ll total 0 -rw-rw-r-- 1 wjmhlh wjmhlh 0 Nov 10 15:07 mytest.c [wjmhlh@VM-12-9-centos lesson8] vim mytest.c [wjmhlh@VM-12-9-centos lesson8] gcc mytest.c -o mytest [wjmhlh@VM-12-9-centos lesson8]

从上面看到,我们形成的可执行程序的大小是8360,也就是大概8k左右。

于是,我们继续看:

[wjmhlh@VM-12-9-centos lesson8]$ file mytest mytest: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=ea8c82beaf490b03226c3200ce7a0cbb54cd0bc2, not stripped

[wjmhlh@VM-12-9-centos lesson8]$ ldd mytest     linux-vdso.so.1 =>  (0x00007ffd7e35d000)  libc.so.6 => /lib64/libc.so.6 (0x00007f88f1346000)     /lib64/ld-linux-x86-64.so.2 (0x00007f88f1714000)

在解释上面的代码前,先要了解一下在Linux下库的命名:

动态库:libXXXXXX.so 以lib为前缀,以.so为后缀,可以带上版本号,XXX是库的名字

静态的:libYYYYYY.a 以lib为前缀,以.a为后缀,可以带上版本号,YYY是库的名字

而要区分是什么库,那么,去掉前缀和后缀,剩下的就是我们需要知道的库。

如上面:libc.so.6——>lib   c  .so.6         最终我们看到的是我们最熟悉的c,也就是c的标准库了,是个动态库,这也说明了,在Linux下,默认的是动态库。

这也是我们写的头文件中的stdio中的std的意思——C标准库,用了标准库。

这时候就会有个疑问?既然我们在stdio.h中使用了C标准库,那么C标准库在哪?咋看不见?

其实就是在lib64/libc.so.6 这里。

[wjmhlh@VM-12-9-centos lesson8] ls /lib64/libc.so.6/lib64/libc.so.6 [wjmhlh@VM-12-9-centos lesson8] ls /lib64/libc.so.6 -l lrwxrwxrwx 1 root root 12 Jul 25 16:58 /lib64/libc.so.6 -> libc-2.17.so  

于是,我们可以查看我们的库了。

[wjmhlh@VM-12-9-centos lesson8]$ ls /lib64/libc-2.17.so -al -rwxr-xr-x 1 root root 2156592 May 19 00:18 /lib64/libc-2.17.so 其中,2156592就是库的大小

如果我们需要静态库了,那咋办?

我们可以在gcc的时候,在后面加上-static。

[wjmhlh@VM-12-9-centos lesson8] gcc mytest.c -o mytest_s -static [wjmhlh@VM-12-9-centos lesson8] ll total 860 -rwxrwxr-x 1 wjmhlh wjmhlh   8360 Nov 10 15:09 mytest -rw-rw-r-- 1 wjmhlh wjmhlh    210 Nov 10 15:08 mytest.c -rwxrwxr-x 1 wjmhlh wjmhlh 861288 Nov 10 15:26 mytest_s  

 我们来查看一下它所使用的库:

[wjmhlh@VM-12-9-centos lesson8]$ file mytest_s mytest_s: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.32, BuildID[sha1]=b9f8bdddd842b61ac994be5e91eff1d7e3e64234, not stripped [wjmhlh@VM-12-9-centos lesson8]$ ldd mytest_s     not a dynamic executable-------不是可动态执行的 可以看到,确实是静态库了,使用了静态链接,同时,我们可以看到静态链接和动态链接的体积区别

静态:861288

动态:8360

一百倍!!!😀要是我需要下载的软件,一家公司是8360大小,另外一家是861288大小,功能等等几乎一样,我肯定选8360啊!!!

其实,在这里我们就能继续看到,我们在Linux的指令,其实都是动态库中的。

[wjmhlh@VM-12-9-centos lesson8] file /usr/bin/ls /usr/bin/ls: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=c8ada1f7095f6b2bb7ddc848e088c2d615c3743e, stripped [wjmhlh@VM-12-9-centos lesson8] file /usr/bin/pwd /usr/bin/pwd: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=8f1d0ff9fee13b5d44a1189122856912af0486bc, stripped  

所有,千万不要删掉系统自带的动态库!!!而且,这个库只有一份,意味着所有用C语言写的程序,不会出现重复的库代码,因此动态库也叫共享库。因此,要是去下载一个C写的程序,也不需要下载C标准库了。

而静态库呢?在执行静态链接的时候,拷贝的是.so内部的代码吗?

NO!系统里面必须存在.a结尾的静态库。

[wjmhlh@VM-12-9-centos lesson8] ls /lib64/libc.a/lib64/libc.a [wjmhlh@VM-12-9-centos lesson8] ll /lib64/libc.a -rw-r--r-- 1 root root 5105516 May 19 00:18 /lib64/libc.a  

这个就是C语言的静态库了。在静态链接的时候,会将静态库的代码拷贝过去。一般来说,系统会自带动态库,而静态库需要自己安装。

同样的对于c++,一样有自己的C++标准库,动静态库。

[wjmhlh@VM-12-9-centos lesson8] touch mytest.cpp [wjmhlh@VM-12-9-centos lesson8] ll total 4 -rw-rw-r-- 1 wjmhlh wjmhlh 210 Nov 10 15:08 mytest.c -rw-rw-r-- 1 wjmhlh wjmhlh   0 Nov 10 15:42 mytest.cpp [wjmhlh@VM-12-9-centos lesson8] vim mytest.cpp [wjmhlh@VM-12-9-centos lesson8] ll total 8 -rw-rw-r-- 1 wjmhlh wjmhlh 210 Nov 10 15:08 mytest.c -rw-rw-r-- 1 wjmhlh wjmhlh 188 Nov 10 15:43 mytest.cpp [wjmhlh@VM-12-9-centos lesson8] g++ mytest.cpp -o mytestcpp [wjmhlh@VM-12-9-centos lesson8] ldd mytestcpp     linux-vdso.so.1 =>  (0x00007ffff6c62000)    libstdc++.so.6 => /home/wjmhlh/.VimForCpp/vim/bundle/YCM.so/el7.x86_64/libstdc++.so.6 (0x00007f42d0d10000)     libm.so.6 => /lib64/libm.so.6 (0x00007f42d0a0e000)     libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007f42d07f8000)     libc.so.6 => /lib64/libc.so.6 (0x00007f42d042a000)     /lib64/ld-linux-x86-64.so.2 (0x00007f42d1091000)  

wjmhlh@VM-12-9-centos lesson8] g++ mytest.cpp -o mytestcpp -static [wjmhlh@VM-12-9-centos lesson8] ll total 1580 -rw-rw-r-- 1 wjmhlh wjmhlh     210 Nov 10 15:08 mytest.c -rwxrwxr-x 1 wjmhlh wjmhlh 1608376 Nov 10 15:44 mytestcpp -rw-rw-r-- 1 wjmhlh wjmhlh     188 Nov 10 15:43 mytest.cpp [wjmhlh@VM-12-9-centos lesson8] ldd mytestcpp     not a dynamic executable [wjmhlh@VM-12-9-centos lesson8] ll total 1580 -rw-rw-r-- 1 wjmhlh wjmhlh     210 Nov 10 15:08 mytest.c -rwxrwxr-x 1 wjmhlh wjmhlh 1608376 Nov 10 15:44 mytestcpp -rw-rw-r-- 1 wjmhlh wjmhlh     188 Nov 10 15:43 mytest.cpp [wjmhlh@VM-12-9-centos lesson8]

最后:系统本身为了支持我们去编程,给我们提供了标准库的.h(接口),标准的动静态库.so/.a(实现方法)。所以,我们的代码+库代码==可执行程序!

其实,上述的内容,在windows下的原理差不多,也需要动静态库。windows的动态库的后缀:.dll,静态库:.lib。

2.Makefile

makefile是什么?

makefile是一个工具,可以"自动化编译",只需要一个make命令,整个工程就会完全自动编译,大大地提高软件开发效率。

makefile是一个文件,make是一个命令。

如何使用makefile?注:可以大写,可以小写。

首先:创建makefile文件,文件名必须的是makefile

[wjmhlh@VM-12-9-centos mk] touch makefile [wjmhlh@VM-12-9-centos mk] ll total 4 -rw-rw-r-- 1 wjmhlh wjmhlh  0 Nov 10 23:40 makefile  

然后:vim makefile

要往里面写入依赖关系和依赖方法:

保存退出。之后,我们不用再使用gcc或g++了,直接使用make命令,完成自动编译

[wjmhlh@VM-12-9-centos mk] makegcc mycode.c -o mycode  [wjmhlh@VM-12-9-centos mk] cat makefilemycode:mycode.c     gcc mycode.c -o mycode   

-rw-rw-r-- 1 wjmhlh wjmhlh   42 Nov 10 23:44 makefile -rwxrwxr-x 1 wjmhlh wjmhlh 8360 Nov 10 23:43 mycode -rw-rw-r-- 1 wjmhlh wjmhlh   81 Nov 10 23:39 mycode.c [wjmhlh@VM-12-9-centos mk]$ ./mycode hello makefile

如果这样子来看,其实反应不出来makefile的作用如何之大。因为工程量少。等工程量大,就知道了。因此现在所知道的作用,便是再makfile里面写gcc,等以后随便写代码后,直接make,不需要gcc,方便了!

makefile原理:

mycode:mycode.c     gcc mycode.c -o mycode  根据上面解释的依赖方法和依赖关系:mycode是要形成的可执行程序,mycode是mycode.c的儿子,因此mycode.c的mycode的依赖对象,而形成的(依赖)方法便是 gcc mycode.c -o mycode 。 目的:依赖对象         依赖方法 ↑ 这里的空格,必须以table为开头!必须以table为开头!必须以table为开头!

因为makefile是文件,因此我们需要知道的是,makefile内部,要写的就是依赖方法和依赖对象!

注意:是文件,不是目录(文件夹)。创建的时候,是touch makefile,不是mkdir makefile。

[wjmhlh@VM-12-9-centos mk] touch makefile [wjmhlh@VM-12-9-centos mk] vim makefile [wjmhlh@VM-12-9-centos mk] makegcc mycode.c -o mycode [wjmhlh@VM-12-9-centos mk] ll total 20 -rw-rw-r-- 1 wjmhlh wjmhlh   40 Nov 11 10:03 makefile -rwxrwxr-x 1 wjmhlh wjmhlh 8360 Nov 11 10:03 mycode -rw-rw-r-- 1 wjmhlh wjmhlh   81 Nov 10 23:39 mycode.c [wjmhlh@VM-12-9-centos mk]

如果有一天,我不想要这个可执行程序了,那么改如何清理呢?

我们可以再下面写上clean:

从此:想要编译:make   想要清理已经编译好的可执行程序:make clean

-rw-rw-r-- 1 wjmhlh wjmhlh   77 Nov 11 10:12 makefile -rwxrwxr-x 1 wjmhlh wjmhlh 8360 Nov 11 10:03 mycode -rw-rw-r-- 1 wjmhlh wjmhlh   81 Nov 10 23:39 mycode.c [wjmhlh@VM-12-9-centos mk] make cleanrm -f mycode  [wjmhlh@VM-12-9-centos mk] ll total 8 -rw-rw-r-- 1 wjmhlh wjmhlh 77 Nov 11 10:12 makefile -rw-rw-r-- 1 wjmhlh wjmhlh 81 Nov 10 23:39 mycode.c  

具体解释:

①clean不依赖任何对象,是独立存在的。因此直接换行写下一行。

clean:                         rm -f mycode

rm -f mycode 是clean的依赖方法。

而为了表明clean是清理这里的项目,那么加上.PHONY:clean,来代表伪目标;

伪目标是什么?

.PHONY:被该关键字修饰的对象,是一个伪目标

首先来了解一下一些细节:

当我们make一个可执行程序后,如果继续make,它就会显示,已经是最新的可执行程序,不需要编译了。

-rw-rw-r-- 1 wjmhlh wjmhlh 77 Nov 11 10:12 makefile -rw-rw-r-- 1 wjmhlh wjmhlh 81 Nov 10 23:39 mycode.c [wjmhlh@VM-12-9-centos mk] makegcc mycode.c -o mycode  [wjmhlh@VM-12-9-centos mk] makemake: `mycode' is up to date. [wjmhlh@VM-12-9-centos mk] makemake: `mycode' is up to date. [wjmhlh@VM-12-9-centos mk] makemake: `mycode' is up to date.

如果我们在这种情况下,很久没用这个代码,而这个代码没有任何修改,不需要再make。

如果我们在这种情况下,去改写代码,那么我们还需要再make一下吗?需要。

换句话说:同样的代码,只需要make一次。

但是我们将同样的思路去这些make clean,却发现,make clean可以一直被执行,即使可执行程序已经被清理了,但还是可以执行make clean。

[wjmhlh@VM-12-9-centos mk] make cleanrm -f mycode  [wjmhlh@VM-12-9-centos mk] ll total 8 -rw-rw-r-- 1 wjmhlh wjmhlh 77 Nov 11 10:12 makefile -rw-rw-r-- 1 wjmhlh wjmhlh 81 Nov 10 23:39 mycode.c [wjmhlh@VM-12-9-centos mk] make cleanrm -f mycode  [wjmhlh@VM-12-9-centos mk] make cleanrm -f mycode  [wjmhlh@VM-12-9-centos mk]

为什么会这样?

其实,这就是.PHONY 的作用:伪目标代表的含义就是:该目标,总是被执行的!

我们试一下,用.PHONY来修饰mycode(目标)

-rw-rw-r-- 1 wjmhlh wjmhlh 91 Nov 11 10:23 makefile -rw-rw-r-- 1 wjmhlh wjmhlh 81 Nov 10 23:39 mycode.c [wjmhlh@VM-12-9-centos mk] makegcc mycode.c -o mycode  [wjmhlh@VM-12-9-centos mk] ll total 20 -rw-rw-r-- 1 wjmhlh wjmhlh   91 Nov 11 10:23 makefile -rwxrwxr-x 1 wjmhlh wjmhlh 8360 Nov 11 10:24 mycode -rw-rw-r-- 1 wjmhlh wjmhlh   81 Nov 10 23:39 mycode.c [wjmhlh@VM-12-9-centos mk] makegcc mycode.c -o mycode  [wjmhlh@VM-12-9-centos mk] makegcc mycode.c -o mycode  [wjmhlh@VM-12-9-centos mk] makegcc mycode.c -o mycode  [wjmhlh@VM-12-9-centos mk] ll total 20 -rw-rw-r-- 1 wjmhlh wjmhlh   91 Nov 11 10:23 makefile -rwxrwxr-x 1 wjmhlh wjmhlh 8360 Nov 11 10:24 mycode -rw-rw-r-- 1 wjmhlh wjmhlh   81 Nov 10 23:39 mycode.c  

 mycode变成了总是能够被执行的目标了。

我们一般不会将我们所需要的可执行程序用.PHONY来修饰,因为只要代码没修改过,只需要make一次就可以了。

其实我特别好奇:gcc是如何得知我不需要再编译了呢?

这就需要提到三个时间了

[wjmhlh@VM-12-9-centos mk]$ stat mycode.c   File: ‘mycode.c’   Size: 81            Blocks: 8          IO Block: 4096   regular file Device: fd01h/64769d    Inode: 789551      Links: 1 Access: (0664/-rw-rw-r--)  Uid: ( 1001/  wjmhlh)   Gid: ( 1001/  wjmhlh) Access: 2022-11-10 23:39:57.210243832 +0800 Modify: 2022-11-10 23:39:55.912237391 +0800 Change: 2022-11-10 23:39:55.912237391 +0800

[wjmhlh@VM-12-9-centos mk]$ stat mycode   File: ‘mycode’   Size: 8360          Blocks: 24         IO Block: 4096   regular file Device: fd01h/64769d    Inode: 789553      Links: 1 Access: (0775/-rwxrwxr-x)  Uid: ( 1001/  wjmhlh)   Gid: ( 1001/  wjmhlh) Access: 2022-11-11 10:24:14.205381640 +0800 Modify: 2022-11-11 10:24:13.801379696 +0800 Change: 2022-11-11 10:24:13.801379696 +0800

①Modify:指文件内容被修改的时间

②Change:指文件属性被修改的时间

③Access:指文件被访问后的时间

例如:我们去改文件属性:

当前的mycode的Change:2022-11-11 10:24:13.801379696 +0800

其他两个时间:

Access: 2022-11-11 10:24:14.205381640 +0800 Modify: 2022-11-11 10:24:13.801379696 +0800

-rw-rw-r-- 1 wjmhlh wjmhlh   77 Nov 11 10:28 makefile-rwxrwxr-x 1 wjmhlh wjmhlh 8360 Nov 11 10:24 mycode -rw-rw-r-- 1 wjmhlh wjmhlh   81 Nov 10 23:39 mycode.c [wjmhlh@VM-12-9-centos mk] chmod u-w mycode [wjmhlh@VM-12-9-centos mk] ll total 20 -rw-rw-r-- 1 wjmhlh wjmhlh   77 Nov 11 10:28 makefile-r-xrwxr-x 1 wjmhlh wjmhlh 8360 Nov 11 10:24 mycode -rw-rw-r-- 1 wjmhlh wjmhlh   81 Nov 10 23:39 mycode.c [wjmhlh@VM-12-9-centos mk]

此时的时间:

Access: 2022-11-11 10:24:14.205381640 +0800 Modify: 2022-11-11 10:24:13.801379696 +0800 Change: 2022-11-11 10:32:35.103783432 +0800

可以得知:这里的Change的时间被改变了,其他两个没有发生改变

再看对文件内容的修改:

[wjmhlh@VM-12-9-centos mk] vim mycode.c [wjmhlh@VM-12-9-centos mk] make gcc mycode.c -o mycode  [wjmhlh@VM-12-9-centos mk]

同理,这里会对Modify的时间做修改,但是我们可以看见的是,Change的时间也被修改了。

这是为什么?

因为对内容改变了,文件的大小也改变了,即属性也改变了。

这里很合理,但不可思议的是,Access为啥不变(我举的这个例子,因为我make了一下,时间间隔比较长,所以改变了,其实是没变的。)

Access代表是,访问这个文件的时间。

其实访问它,确实是会变,但是呢,如果在短时间内(非常短的时间),它不会变。即不会频繁去改变时间。其原因是,我们会频繁地访问文件,如果频繁地去修改时间,意味着要非常多次地去访问磁盘,是一个负担!而且,很少人去关心文件被访问的时间,而且去关心文件的内容属性被修改的时间。

回到那个问题:gcc如何得知不需要再编译可执行程序?

首先:我们需要知道的是,一定是现有源文件,再有可执行程序。所以!在编译完成,生产可执行程序后,源文件的Modify时间一定比可执行程序的Modify时间早!

于是,我们就知道了,gcc是通过比较两个时间来得知是否需要重新编译。

如果源文件的Modify比可执行程序的Modify早,说明,代码没有被修改,不需要make重新编译。

如果源文件的Modify比可执行程序的Modify晚,说明代码被修改,需要make重新编译。

此时,我们可以去想想,为什么加了.PHONHY后,总是被执行,.PHONY是怎么做的?

其实原理就是跟touch一下已有的源文件(可以理解为摸一下文件,导致文件时间改变了)一样。所以加了.PHONY的作用就是,不要再去对比时间了,直接给我make!

再来看看新的问题:为什么直接输入make,会自己识别是编译可执行程序?而不是清理。清理则需要输入make clean?

编译可执行程序的时候,我们其实也可以写成make mycode。只不过呢?make这个命令,是从上到下,按顺序执行。

也就是说,第一个被make碰到的目标文件,可以省略掉目标文件名,仅此而已。

但这里也有个问题?不是说好make是按顺序往下走吗?怎么make了后,只执行了一次?而没有继续往下走了呢?

默认情况下:makefile只形成一个目标文件,那么后续的,需要再此输入命令。

makefile的推到规则:

其实在写makefile时,mycode依赖的不是mycode.c,而是mycode.o;

而mycode.o依赖的是mycode.s;

mycode.s依赖的是mycode.i;

mycode.i依赖的是mycode.c 

在执行make命令的时候,从上到下扫描,要生成mycode,找mycode.o,没有mycode.o,那就找往下找,一直找。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3