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

简介: Linux中关于gcc/g++的基本操作,以及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]$ ./a.out
hello1
hello2
hello3

直接使用语句: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那一行代码,这就是条件编译;

XSTJ2S})45@GAE$9`9B0NU1.png

[wjmhlh@VM-12-9-centos lesson7]$ ./a.out

hello1

hello2

hello3

hello deafult

宏: 234

 

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

OFEGWSK@QU_(SE11Q7V0[4W.png

以下是结果:

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]$ vim test.s

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

K1@0DHG3UB3IG19}7E0A[SX.png

此时,已经将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]$ vim test.o

 

X%])(U3UZQDQ8IRU`F1HNBC.png

虽然变成了二进制,但是还是不能被执行,因为少了链接,没有将库结合起来。因为这三部,都只在编译我的代码,并没有带上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]$ ./mytest

hello1

hello2

hello3

hello deafult

宏: 234

在链接这一步:

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

我们的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]$ ll

total 16

-rwxrwxr-x 1 wjmhlh wjmhlh 8360 Nov 10 15:09 mytest

-rw-rw-r-- 1 wjmhlh wjmhlh  210 Nov 10 15:08 mytest.c

 

从上面看到,我们形成的可执行程序的大小是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]$ file mytestcpp

mytestcpp: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.32, BuildID[sha1]=58b7771454ee8e14960bf100fb6376db5d8a9a03, not stripped

 

最后:系统本身为了支持我们去编程,给我们提供了标准库的.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

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

%C()27FQ85{3S$M{IX72SUV.png

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

[wjmhlh@VM-12-9-centos mk]$ make

gcc mycode.c -o mycode

[wjmhlh@VM-12-9-centos mk]$ cat makefile

mycode: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原理:

{[GUO%R4M13TMVG3E@~3X07.png

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]$ make

gcc 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]$ ./mycode

hello makefile

 

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

我们可以再下面写上clean:

P52@DEF`QQ8{(Y(892DTWJN.png

从此:想要编译: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 clean

rm -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]$ make

gcc mycode.c -o mycode

[wjmhlh@VM-12-9-centos mk]$ make

make: `mycode' is up to date.

[wjmhlh@VM-12-9-centos mk]$ make

make: `mycode' is up to date.

[wjmhlh@VM-12-9-centos mk]$ make

make: `mycode' is up to date.

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

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

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

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

[wjmhlh@VM-12-9-centos mk]$ make clean

rm -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 clean

rm -f mycode

[wjmhlh@VM-12-9-centos mk]$ make clean

rm -f mycode

[wjmhlh@VM-12-9-centos mk]$ make clean

rm -f mycode

 

为什么会这样?

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

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

`2C2`P~Q)IAP`Y$XNSGRS7I.png

-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]$ make

gcc 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]$ make

gcc mycode.c -o mycode

[wjmhlh@VM-12-9-centos mk]$ make

gcc mycode.c -o mycode

[wjmhlh@VM-12-9-centos mk]$ make

gcc 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]$ stat mycode

 File: ‘mycode’

 Size: 8360          Blocks: 24         IO Block: 4096   regular file

Device: fd01h/64769d    Inode: 789553      Links: 1

Access: (0575/-r-xrwxr-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:32:35.103783432 +0800

 

此时的时间:

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]$ 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:36:36.216934574 +0800
Modify: 2022-11-11 10:36:35.814932656 +0800
Change: 2022-11-11 10:36:35.814932656 +0800

同理,这里会对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

_KH@U)9}DV5XFSJVUOAG88V.png

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


相关文章
|
1月前
|
Linux API 开发工具
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
ijkplayer是由B站研发的移动端播放器,基于FFmpeg 3.4,支持Android和iOS。其源码托管于GitHub,截至2024年9月15日,获得了3.24万星标和0.81万分支,尽管已停止更新6年。本文档介绍了如何在Linux环境下编译ijkplayer的so库,以便在较新的开发环境中使用。首先需安装编译工具并调整/tmp分区大小,接着下载并安装Android SDK和NDK,最后下载ijkplayer源码并编译。详细步骤包括环境准备、工具安装及库编译等。更多FFmpeg开发知识可参考相关书籍。
85 0
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
|
1月前
|
Linux 编译器 C语言
【Linux快速入门(一)】Linux与ROS学习之编译基础(gcc编译)
【Linux快速入门(一)】Linux与ROS学习之编译基础(gcc编译)
|
20天前
|
Linux
Linux - 如何编译源码安装软件
源码编译安装通常包括三个步骤:1) `./configure` 检测平台特征和依赖项,生成 Makefile;2) `make` 编译源码,生成可执行文件;3) `make install` 将可执行文件安装到指定目录并配置环境变量。
36 0
|
1月前
|
Linux 开发工具
【Linux快速入门(二)】Linux与ROS学习之编译基础(make编译)
【Linux快速入门(二)】Linux与ROS学习之编译基础(make编译)
|
7天前
|
监控 Linux
如何检查 Linux 内存使用量是否耗尽?这 5 个命令堪称绝了!
本文介绍了在Linux系统中检查内存使用情况的5个常用命令:`free`、`top`、`vmstat`、`pidstat` 和 `/proc/meminfo` 文件,帮助用户准确监控内存状态,确保系统稳定运行。
64 6
|
8天前
|
Linux
在 Linux 系统中,“cd”命令用于切换当前工作目录
在 Linux 系统中,“cd”命令用于切换当前工作目录。本文详细介绍了“cd”命令的基本用法和常见技巧,包括使用“.”、“..”、“~”、绝对路径和相对路径,以及快速切换到上一次工作目录等。此外,还探讨了高级技巧,如使用通配符、结合其他命令、在脚本中使用,以及实际应用案例,帮助读者提高工作效率。
34 3
|
8天前
|
监控 安全 Linux
在 Linux 系统中,网络管理是重要任务。本文介绍了常用的网络命令及其适用场景
在 Linux 系统中,网络管理是重要任务。本文介绍了常用的网络命令及其适用场景,包括 ping(测试连通性)、traceroute(跟踪路由路径)、netstat(显示网络连接信息)、nmap(网络扫描)、ifconfig 和 ip(网络接口配置)。掌握这些命令有助于高效诊断和解决网络问题,保障网络稳定运行。
26 2
|
15天前
|
缓存 监控 Linux
|
3天前
|
运维 监控 网络协议
运维工程师日常工作中最常用的20个Linux命令,涵盖文件操作、目录管理、权限设置、系统监控等方面
本文介绍了运维工程师日常工作中最常用的20个Linux命令,涵盖文件操作、目录管理、权限设置、系统监控等方面,旨在帮助读者提高工作效率。从基本的文件查看与编辑,到高级的网络配置与安全管理,这些命令是运维工作中的必备工具。
17 3
下一篇
无影云桌面