8.1.1 编译、安装、打印HelloWorld程序
本节将带领大家完成从源代码编写到源码编译,再到程序安装的过程,希望通过该过程的学习,能让大家了解源码编译安装的原理。由于几乎所有的开源程序使用的都是C语言,所以这里也使用C语言来演示如何编写、编译、安装一个打印“Hello,world!”程序。
首先,根据软件需求写出源代码(本例中只要求能打印出HelloWorld)。可使用vi编译器编写HelloWorld.c文件,方式如下(常见编辑器包括vi编辑器的用法下一章中将具体讲):
- [root@localhost ~]# vi HelloWorld.c #
- 回车
- #
- 这里将进入vi
- 命令模式,按i
- 键进入编辑模式,输入如下内容
- #
- 输入完成后,按Esc
- 键,然后输入冒号,再按x
- 键并按回车键
- #include <stdio.h>
- int main(void) {
- printf("Hello,world!\n");
- return 0;
- }
有了HelloWorld.c这个源码文件,下面就需要使用gcc工具将该源代码编译成一个可执行的二进制程序了。如果你目前使用的Linux完全是按照第1章演示的安装过程安装的,那么很有可能系统中并没有gcc命令,要想安装gcc请参考下一节中的“使用rpm包安装gcc”;如果确认系统中存在该命令,则可以使用如下命令将源代码编译为可执行的二进制文件:
- [root@localhost ~]# gcc HelloWorld.c -o HelloWorld
- #
- 如果没有gcc
- 命令,将会出现如下报错信息,否则将生成文件HelloWorld
- #-bash: gcc: command not found
- [root@localhost ~]# ls HelloWorld #
- 得到了HelloWorld
- 二进制文件
- HelloWorld
编译完成后,我们得到了二进制可执行文件HelloWorld。那么接下来是否可以直接运行这个命令呢?答案是否定的,尝试运行该命令的时候,系统给出了command not found的报错信息,如下所示:
- [root@localhost ~]# HelloWorld
- -bash: HelloWorld: command not found
系统中确实已经有了HelloWorld这个程序,可为什么还是说不存在这个命令呢?这就不得不说到系统变量PATH了,它被称作为Linux系统的“环境变量”,可使用如下命令查看当前PATH变量定义的内容:
- [root@localhost ~]# echo $PATH
- usrkerberos/sbin:usrkerberos/bin:usrlocal/sbin:usrlocal/
- bin:/sbin:/bin:usrsbin:usrbin:rootbin
可以看到,PATH变量中是一些由冒号隔开的路径,当输入一个命令时,系统会到PATH所定义的路径中去寻找该命令,找到后就会执行该命令。也就是说,本例中在输入命令HelloWorld并按回车键时,系统先从目录usrkerberos/sbin中寻找是否有这个文件,如果找不到就继续从目录usrkerberos/bin中寻找,再找不到就到目录usrlocal/sbin中找,以此类推。如果在PATH定义的所有目录中都找不到该文件,这时系统就会提示command not found。
想象一下,如果不使用这种机制,那么运行任何命令都需要键入某个命令的全路径,将非常麻烦。还记得在第3章中学习过的which命令吗?它的工作原理也是到环境变量PATH中寻找某个命令,事实上如果使用which找不到某个命令,则说明该命令由于找不到而无法被执行:
- [root@localhost ~]# which HelloWorld
- usrbin/which: no HelloWorld in (usrkerberos/sbin:usrkerberos/
- bin:usrlocal/sbin:usrlocal/bin:/sbin:/bin:usrsbin:usrbin:rootbin)
由于程序HelloWorld当前所在的路径是rootHelloWorld,它并不存在于当前PATH中,所以这里的报错是正常的。解决这里出现的command not found错误有三种方法,第一种方法是在/root目录中使用./HelloWorld执行该命令,或者引用该命令的全路径来执行;第二种方法是将HelloWorld复制到任意一个当前PATH变量包含的目录中;第三种方法是将/root目录追加到PATH变量中。以上方法任意选择使用一种即可,如下所示:
- #
- 第一种方法
- #
- 使用./
- 执行或使用全路径执行
- [root@localhost ]# pwd
- /root
- [root@localhost ]# ./HelloWorld
- Hello, world!
- [root@localhost ]# rootHelloWorld
- Hello, world!
- #
- 第二种方法
- #
- 将HelloWorld
- 复制到任一PATH
- 变量包含的目录中,这里使用/bin
- 目录
- [root@localhost ]# cp HelloWorld bin
- [root@localhost ]# which HelloWorld
- binHelloWorld
- [root@localhost ]# HelloWorld
- Hello, world!
- #
- 第三种方法
- #
- 将/root
- 目录追加到PATH
- 变量中,注意看追加目录的方法
- #
- 在尝试使用该方法之前,如果已经使用了第二种方法,则先删除之前复制的文件
- #[root@localhost ]# rm binHelloWorld
- #rm: remove regular file `binHelloWorld'? y
- [root@localhost ]# export PATH=$PATH:/root
- [root@localhost ]# which HelloWorld
- rootHelloWorld
- [root@localhost ]# HelloWorld
- Hello, world!
此处需要注意的是,虽然第三种方法和第二种方法的原理是一致的,但是第三种方法一般在重启主机或重新登录之后就失效了,原因是这种方法并没有将所定义的环境变量保存到任何配置文件中。在这种方法下,可以使用以下命令保存变量PATH的值:
- echo "export PATH=$PATH:/root" >> etcrc.local
以上就是源码编译安装软件的原理,简单总结一下就是编写源代码→编译源码生成二进制可执行性文件(也就是程序)→复制该文件到任一PATH变量包含的目录中。
本例中用于演示的软件功能十分简单,只要能打印“Hello,world!”就可以了,所以只需要一个单独的源码文件就可以搞定,而且代码也非常简单。在实际工作中,软件的需求往往比较复杂,而且大多是基于模块化开发的思想来实现的,所以一个软件往往需要多个源码文件和各类配置文件,在编译的时候也需要严格按照一定的过程进行编译,比如说需要先编译出某些模块文件之后,才能最终编译并生成主程序。而这个过程也只有软件开发者自己才清楚,这意味着只有在软件开发者提供了详细的编译步骤文档的前提下,拿到该源码包的人才能按照其规定的编译顺序来编译生成程序。在这种情况下,为了方便软件安装,可以使用Makefile简化整个过程,由于本书并不涉及C语言开发以及Makefile的语法,所以这里并不打算深入讲解Makefile,只做一些演示。在/root目录中编辑Makefile,内容如下所示:
- [root@localhost ~]# cat Makefile
- HelloWorld:HelloWorld.o
- gcc -o HelloWorld HelloWorld.c #
- 前面不是空格,而是一个Tab
- install:
- cp HelloWorld bin #
- 此处前面的也是一个Tab
有了Makefile之后,编译安装HelloWorld程序就变得更简单了,只需要以下两条命令即可:
- #
- 第一步,输入make
- 命令,这会自动完成编译的过程
- [root@localhost ~]# make
- gcc -o HelloWorld HelloWorld.c #
- 这里是make
- 命令执行后的输出
- #
- 第二步,输入make install
- 命令,这会自动完成软件的复制
- [root@localhost ]# make install
- cp HelloWorld bin #
- 注意这里是make install
- 的输出,不需要人工复制
- #
- 然后就可以直接执行命令了
- [root@localhost ]# HelloWorld
- Hello, world!
事实上,有很多开源软件自身是不包含Makefile的,特别是在模块化程度较高的软件中,都不包含Makefile,而需要用户根据具体的需求使用软件包目录中的configure工具,生成适合用户特定需求的Makefile,所以典型的源码编译安装软件的过程包括以下3步:
第一步,运行configure命令,并结合必要的参数以生成Makefile;
第二步,运行make命令生成各类模块和主程序;
第三步,运行make install命令将必要的文件复制到安装目录中。
以上3步都需要在对应软件包目录的根目录下运行。