转自:http://www.imooc.com/wenda/detail/242044

    学习了一段时间的Linux,相信大家都会遇到需要安装软件的情况,而新手(比如我)对于这个问题肯定是特别头疼。照着网上的步骤装,可能出现莫名其妙的 错误,不知道如何是好。更关键的是不知道每一步都是在干嘛,所以即使我们依葫芦画瓢地装了多次某个软件,遇到问题也没有办法解决。那今天我就来谈谈 Linux上源码安装软件的一些东西,我也是新手,勿喷……


    首先我们应该知道,计算机能够识别的只有二进制,也就是010101这样的串,或者说是高低电平……

    那么我们平时敲的代码,比如说C语言打印一个"Hello world",相信大家都无比熟悉吧。

#include<stdio.h>
int main()
{
   printf("Hello world!");
   return 0;
}

我 们把上面的文件保存为hello.c。对于计算机来说这只是一个文本文件,里面存的这些英文字母和符号它是看不懂的,对它来说就像天书一样不知所云。那我 们要让这段代码可以执行,就必须让计算机能看懂它。比如我们可以把这段代码用汇编语言写一遍,虽然更贴近底层,但计算机依然看不懂,我们还是得把它写成机 器码才行。

WTF!这我哪儿会啊!!不用担心,我们不会,但是有大牛会,而且他们已经做得非常棒了,那就是大名鼎鼎的gcc。

gcc就是我们常说的编译器(事实上这只是它的一小部分功能),它能把我们人能看懂的C语言代码翻译成机器能够看懂的二进制代码。这样这段代码就能在计算机上愉快地运行了。


那我们现在用gcc来编译一下hello.c

gcc hello.c

执行后我们来看看

Linux【学习心得】深入剖析软件的源码安装


可以发现新生成了一个可执行文件a.out

我们可以运行一下它试试

./a.out


Linux【学习心得】深入剖析软件的源码安装

可以看到成功地输出了"Hello world!"也就是说gcc把我们的C语言文件成功翻译成了计算机能够识别并运行的二进制文件。


到这里大家可能会比较疑惑,你讲安装软件,讲编译C语言代码干什么。我只想说,你先别着急,这是为后面理解安装过程做铺垫,是非常重要的,理解软件安装的那些步骤在干什么可全看这些了。


上面我们用gcc编译好了一个c文件,虽然就是一条指令,但是其实gcc干了很多事情,我们可以把它分为4步:预处理、翻译成汇编语言、汇编、链接。我们可以这么来看看

首先删除掉刚刚生成的a.out

rm -rf a.out

然后我们执行下面这条指令

gcc -E hello.c -o hello.i


Linux【学习心得】深入剖析软件的源码安装

诶,我们可以看到,生成了一个hello.i文件,我们打开它看看可以发现,其实这个hello.i就是把#include<stdio.h>的stdio.h文件写了进来。

然后我们再执行

gcc -S hello.i -o hello.s


Linux【学习心得】深入剖析软件的源码安装

那么可以看到我们生成了一个hello.s文件,打开它看看

Linux【学习心得】深入剖析软件的源码安装


其实就是把上一步的hello.i翻译成了汇编语言

接着我们运行

gcc -c hello.s -o hello.o


生成了一个hello.o文件,当我们想查看这个文件时系统会提示说这是个二进制文件。

Linux【学习心得】深入剖析软件的源码安装

当然这个文件依然是不能执行的,我们可以用file命令看看,它是没有excutable属性的。我们还缺少最后一步

gcc hello.o -o hello.exe


最后就生成了hello.exe这个可执行文件。在这里我还是想说一下,linux不是靠后缀来区分文件的,后缀只是给人看的。.exe是windows里的可执行文件的后缀,但我们不应该带入太多windows里的东西到linux,所以我其实不推荐给可执行文件加.exe后缀

当然我这里只是方便大家看,所以执行

./hello.exe

就可以打印出"Hello world!"了

Linux【学习心得】深入剖析软件的源码安装


到这里大家应该也看明白了编译一个C语言文件的整个过程了,但是细心的同学可能就会有疑问了:既然hello.o是个二进制文件我们也看不懂,gcc干嘛生成它,为什么不直接从hello.s生成hello.exe?

这真的是一个好问题,也是后面讲Linux下源码安装软件非常核心的地方。那我们就来讲讲这个.o是啥东东


其实在我们实际的工程项目中,一个项目是有很多个c文件的,这些文件会互相调用。比如看这个例子


我要输出一个"Hello world!"但是我想把输出单独写成一个output.c文件供其它文件调用。

先看output.c

#include<stdio.h>
#include "haha.h"
void output()
{
    printf("Hello world!");
}

然后是主程序hello.c

#include<stdio.h>
#include "haha.h"
int main()
{
   output();
   return 0;
}

然后是haha.h

void output();

以上都是C语言需要关心的地方,大家如果这里看不明白不要紧,关键看我之后的操作


首先我们来分别编译两个.c文件为.o

gcc -c hello.c -o hello.o
gcc -c output.c -o output.o

完了之后呢,我们进行一次链接

gcc hello.o output.o -o final


这样我们就得到了一个可执行文件final

上面的内容大家只需要关心最后一句,也就是 

gcc hello.o output.o -o final

hello.o和output.o是两个独立的文件,但是hello会用到output的功能,所以我们把它链接起来,最后生成了一个可执行文件,执行就能输出"Hello world!"啦!。那上面代码没有输出换行所以看着有点别扭,我用箭头标出来了。

Linux【学习心得】深入剖析软件的源码安装

那这么做有什么好处呢?想象一下,我们如果修改某个文件,其它文件没有改变的话,只需要重新编译那一个文件,再链接即可,而不需要重新编译所有的文件。不知道大家装过mysql没有,编译一次要30分钟,要是想做个小小的修改,每次重新编译那得多么痛苦……


至于怎么链接之类的问题不在我们的讨论范畴,有兴趣大家可以上google百度一下。


那 回到的主题,我们要安装一个软件,都会先去下载一个"安装包"。既然是"包"那肯定是有很多源文件的,比如我们的apache,里面有很多c源程序,每个 c程序都实现了不同的功能。所以我们如果要安装apache(即生成一个可执行文件),一定是先各种编译,最后把各种.o文件链接成一个可执行文件。那么 问题来了,几十上百个.c文件我要自己编译再链接岂不是疯了?对,确实会疯掉的!你可能都不知道你编译了哪些没有编译哪些……


所以接下来就会介绍大家可能非常熟悉make。这个玩意儿相信在linux上源码安装过软件的同学都知道而且用过而且几乎每个软件安装都会用到。它到底是用到干什么的呢?其实啊,它就是来帮我们解决以上问题的,让我们的编译链接更加方便,让世界更加美好。


那为了说明问题,我们再看一个例子

我们的代码可能会调用系统的已经实现过的函数,这时候编译会发生神马呢?

sin.c

#include<stdio.h>
int main()
{
    double v;
    v = sin (3.14 / 6);  //   pi/6
    return 0;
}

这个sin我们是没有实现的而是去调用系统内的sin

那 如果我们直接编译肯定会报'未声明的函数调用',这个大家都知道,所以我们应该怎么做呢?既然我们要调用系统已经实现好的sin,那么我们得知道它在哪 儿?在linux中呢,libm.so里面实现了sin,所以我们需要在编译命令中加入这个路径,也就是告诉编译器:“你要是发现未定义的函数,就去这个 路径里找找”。

所以我们来看看这个libm.so到底在哪儿

locate libm.so

Linux【学习心得】深入剖析软件的源码安装

然后我们编译时加入这个path

gcc sin.c -lm -L/usr/lib64

-l是说要增加library

-m是说寻找名字叫做libm.so的东西,lib是大多数库文件的前缀所以我们省掉了。

-L+path就是说让gcc到这个path下面来找

Linux【学习心得】深入剖析软件的源码安装

这里会爆出一个warning,但是没关系,只要不是error就好,可以看到已经生成了我们想要的a.out

执行它,就得到了我要的滑板鞋,不对,这都是哪跟哪儿啊……就得到了我要的0.5

Linux【学习心得】深入剖析软件的源码安装

所以其实说到这里,大家应该都知道一个软件从源码到可执行文件应该怎么安装了。要是你闲的蛋疼,可以尝试一下用gcc手动安装mysql,不过我祝你幸福……

说了这么多其实我都在为make做铺垫,可以想象啊,一个软件包里的源文件,可能会调用各种系统的库函数,也会有相互的依赖关系,那么我们手动编译起来就会特别麻烦。make就是来简化以上操作的。


make具体干了些神马呢?

首先,make会在当前目录下寻找一个叫做Makefile的文件,比如我们有如下的源文件

Linux【学习心得】深入剖析软件的源码安装

然后我们新建一个Makefile,编辑它

Linux【学习心得】深入剖析软件的源码安装

注意,命令行(2,3行)必须以tab开头

接下来执行make

Linux【学习心得】深入剖析软件的源码安装

可以看到整个过程一气呵成啊。

所以你知道make的方便了吧,一条指令就搞定了所有。

但是又有同学问了,我写shell脚本不也能做到吗?确实能做到,但make还有个强大的功能——diff,用过git的同学都知道,diff用来判断修改过的文件,那么make只会去重新编译修改过的部分,这会节约很多时间。

那 又有好奇的同学要问了,这个Makefile还不是得写,我写一遍Makefile还是得花好长时间啊!这个不用咱们担心,这是软件提供者需要干的。但是 软件提供者也不会直接给我们写Makefile,想一想原因也很简答。例如上面的-L/usr/lib64,libm.so在我的电脑里在这个路径中,但 是其它的可能不这样,要是Makefile写死了不就完蛋了吗?

所以就有了./configure。这是个检测脚本,用来检测咱们的系统有 没有安装软件需要的库函数(某个需要的库函数都会去默认的路径下找),要是缺少某些库函数,那么软件安装就不能成功。所以啊,大家可能都能理解 了./configure里有时候会加上 --xxx=/usr/local是什么意思了吧(xxx库或者依赖包去/usr/local里去找),那么如果./configure提示我们缺少某些 依赖,大家就知道该怎么办了吗?当然是缺哪个就locate一下那个文件的路径然后在./configure的命令中加上路径作为参数咯。那要是 locate没有需要的依赖,再自己去网上下载并安装,这又是一个相同的过程(有木有递归的感觉)

那么如果./configure成功了, 我们就可以用make来编译和链接了。有同学又要问了,你说了./configure,但是make是需要Makefile的呀!不要急,. /configure这个检测脚本如果检测成功了,就会自动生成Makefile,至于怎么生成,生成内容是什么,这是软件开发者的事儿,那你要是有兴趣 可以打开看看。

所以接下来我们执行make,然后就会看到一溜的刷屏,这其实就是make在帮咱们挨个编译源文件然后链接它们。

make 结束以后呢我们可以执行make install,make install会在Makefile里找对应的参数,然后把make成功的可执行文件放到Makefile指定的路径。如果我们不make install,那么生成的可执行文件就依然还在我们当前目录下。

这便是源码安装软件的整个过程了。


所以总结一下,其实最重要的一步就是./configure和./configure出错了怎么解决。大家理解了之后可以试着去源码安装一下LNMP,帮助加深印象。希望本文对大家有所帮助,3Q~