《Linux内核修炼之道》——2.3 自由软件的编译与安装

简介:

本节书摘来自异步社区《Linux内核修炼之道》一书中的第2章,第2.3节,作者:华清远见嵌入式培训中心 任桥伟著,更多章节内容可以访问云栖社区“异步社区”公众号查看

2.3 自由软件的编译与安装

Linux内核修炼之道
在我们使用Linux的过程中,经常会遇到需要自己编译软件的情况,可以说自由软件的百家争鸣本身就是Linux世界不可分割的一部分。因此,本节简单介绍下自由软件的编译与安装过程。

自由软件和私有软件的区别在于它们对源代码的访问不同。自由软件以源代码文件压缩包的形式发布,用户必须自己编译源代码才能使用。

对于大部分的自由软件,存在已编译版本,用户可以只安装这些预编译的二进制代码。而某些自由软件并不以这种形式发布,或者其早期版本不以二进制代码形式发布,而且,如果你使用的是特殊的操作系统或者特殊的硬件架构,许多软件并没有为此提供编译好的版本。更重要的是,亲自编译软件可以让你只启用自己感兴趣的选项,或者通过对该软件源码的修正以便它能够确实满足自己的需求。

2.3.1 发布时的组织结构
通常,自由软件发布时都具有相同的组织结构。

INSTALL文件:描述安装步骤。
README文件:包含有关该软件的一般信息(简短描述、作者、项目URL、相关文档、有用的链接等)。如果不存在INSTALL文件,通常在README文件中会包含安装步骤简介。
COPYING文件:包含许可证或是发布该软件的情形。有时候该文件叫LICENSE。
CONTRIB或是CREDITS文件:包含该软件相关人员的列表(活动的参与者、相关评论、第三方软件等)。
CHANGES文件:包含最近改进以及故障修正,有时为较少见的NEWS文件。
Makefile文件:控制该软件的编译(对make该文件是必需的)。如果该文件一开始不存在,那么它将由预编译配置过程生成。
经常也有configure或Imakefile文件以便用户针对特殊系统自定义生成新的 Makefile文件。
保存源文件以及在编译结束后保存二进制文件的目录,通常为src。
保存与该软件相关的文档(通常是man形式)的目录,通常为doc。
有时也有保存该软件特定数据(一般是配置文件、数据示例文件或资源文件)的目录。
2.3.2 配置
仅从技术兴趣上看,自由软件作者提供源代码的目的在于使该软件可移植,使其能够在现存的类UNIX系统上几乎不加修改地使用,而这需要在编译这些软件之前对其进行配置。

有几种不同的配置方式,但我们需要选择作者指定的那个。

如果在该软件的发布目录中存在一个名为configure的文件,则可以使用AutoConf进行配置。
如果在该软件的发布目录中存在一个名为Imakefile的文件,则可以使用imake进行配置。
根据INSTALL文件(或README文件)的内容提示运行一个shell脚本(比如 install.sh)。
1.AutoConf
AutoConf用于正确配置软件,它创建编译需要的文件(比如Makefile),且有时会直接对源代码进行修改(比如使用config.h.in文件)。AutoConf的规则十分简单。

该软件的作者了解配置他的软件需要进行哪些测试(比如:“你使用哪一版本的库文件?”),他使用一种精确的语法将这些测试编写进configure.in文件。
并且,他通过运行AutoConf从configure.in文件生成configure自动配置脚本,该脚本将在配置软件的时候运行所需的测试。
最终用户执行该脚本,这样AutoConf就会为编译进行配置。
下面是一个AutoConf的使用示例:

# ./configure
loading cache ./config.cache
checking for gcc... gcc
checking whether the C compiler (gcc  ) works... yes
checking whether the C compiler (gcc  ) is a cross-compiler... no
checking whether we are using GNU C... yes
checking whether gcc accepts -g... yes
checking for main in -lX11... yes
checking for main in -lXpm... yes
checking for main in -lguile... yes
checking for main in -lm... yes
checking for main in -lncurses... yes
checking how to run the C preprocessor... gcc -E
checking for X... libraries /usr/X11R6/lib, headers /usr/X11R6/include
checking for ANSI C header files... yes
checking for unistd.h... yes
checking for working const... yes
updating cache ./config.cache
creating ./config.status
creating lib/Makefile
creating src/Makefile
creating Makefile

为了更好地控制configure,可以通过命令行或环境变量为其添加选项。比如:

# ./configure --with-gcc --prefix=/opt/GNU

或者(在bash中):

# export CC=`which gcc`
# export CFLAGS=-O2
# ./configure --with-gcc

或者:

# CC=gcc CFLAGS=-O2 ./configure

一般而言,大部分configure脚本执行失败时的出错信息都类似于“configure: error: Cannot find library guile”,这表示configure脚本找不到某个库文件。configure脚本在测试时会使用该库文件编译一个小测试程序,如果它不能成功编译该程序,就说明它不能够编译该软件,所以会给出该出错信息。出现该错误后,我们可以采取下面的措施。

可以在config.log文件中找到配置过程所执行的每一步骤,从中发现产生这一错误的原因。
检查提示的库文件是否已正确安装。如果没有,在安装之后再运行configure。检查它是否已安装的有效方式是查找包含提示字符串的库文件,一般是lib< 名称 >.so。比如:

# find / -name 'libguile*'

检查编译器是否能够访问该库文件,它是否在/usr/lib、/lib、/usr/X11R6/lib (或是在由环境变量LD_LIBRARY_PATH指出的)目录中。
检查该库文件相应的头文件是否已安装于正确地方(通常是/usr/include或 /usr/local/include或/usr/X11R6/include)。
检查是否拥有足够的磁盘空间(configure脚本需要一些空间来保存中间文件)。可以通过“dh”命令查看系统中各分区的使用情况。
检查是否某些环境变量设置错了,比如LD_LIBRARY_PATH。
2.imake
imake让你能够使用简单的规则生成Makefile文件以配置自由软件,这些规则指定需要编译那些文件才能生成所需的二进制文件,而imake生成相应的Makefile。可在Imakefile文件中指定这些规则。

使用imake最为简单的方法是进入解压后的代码目录,然后运行xmkmf脚本,而它又会调用imake程序。

3.各种shell脚本
阅读INSTALL或README文件了解进一步信息。通常,需要执行install.sh或 configure.sh文件。该安装脚本或是非交互的(它自己决定需要什么),或者会向询问有关系统的信息(比如是路径)。

另外,某些自由软件在其初期开发阶段,有时会要求用户手动更改某些配置文件。通常,这些文件是Makefile文件和config.h文件。

2.3.3 编译
既然该软件已正确配置,所剩的就是编译了。这个阶段通常比较简单,并且不会出现什么严重的问题。

编译源代码的各个步骤常存储于Makefile或GNUMakefile文件中。当执行make时,它会从当前目录读取该文件(如果该文件存在的话)。如果它不存在,可以通过make的-f选项指定其他文件。

通常,使用make需要遵循如下一些约定。

不加参数执行make表示仅编译程序,而不安装。
make install编译程序(不是一定的),并随后将文件安装到系统的正确位置。某些文件常常无法正确安装(比如man、info),可能会需要用户自己手动复制。有时候,需要在子目录中再次执行make install,通常这是由于包含了第三方开发的模块。
make clean清除编译产生的所有临时文件,大多数情况下还会删除可执行文件。
编译时最为常见的错误有:

(1)main.c:16: decl.h: No such file or directory

编译器不能找到对应的头文件。不过,在软件配置阶段就可能已经发现该错误了。解决方法如下。

检查该头文件确实已经存在于以下某个目录中:/usr/include、/usr/local/include、/usr/X11R6/include或它们的某个子目录。如果没有,请在整个磁盘上查找它(可以使用find或locate命令)。如果还是没有,请检查是否已经安装了该头文件所对应的库。
检查该头文件确实可以读取(可以使用less命令来测试)。
如果它确实在/usr/local/include或/usr/X11R6/include目录中,你可能需要为自己的编译器添加额外的参数,打开编译出错的那个目录中的那个Makefile文件,找到出错的那一行,并在调用编译器(gcc,有时是$(CC))的地方添加字符串-I< 路径 >,其中< 路径 >是包含该头文件的路径。如果不清楚要把该选项加到哪里,就把它添加到文件开始处CFLAGS或CC变量定义后面。
再次运行make,如果它还是不起作用,请检查前面的那个选项是否被添加于编译过程中出错的地方。
如果还是不起作用,只有向周围的高手或开发社区求助了。
(2)'struct foo' undeclared (first use this function)

结构几乎是所有软件都会使用的一种数据类型,系统在头文件中可能会定义许多。这个错误提示表示该问题可能是由于找不到头文件,或误用头文件所造成。解决该问题的正确步骤如下。

试着检查一下出问题的结构是否已由程序或系统定义,比如用grep命令查看该结构是否已经在某个头文件中定义了。比如,进入该软件源代码的根目录中执行:

# find . -name '*.h'| xargs grep 'struct foo' | less

在屏幕上可能会出现许多行,找到头文件中grep指出的那一行,检查它是否就是你所需要的。如果是,则说明该头文件未包含于出错的.c文件中。有两个解决方法:在出错的.c文件开始处添加#include "< 文件名 >.h";或者将该结构的定义复制到该文件的开始处。

如果没有找到,在系统头文件(通常位于/usr/include、/usr/X11R6/include或/usr/local/include)中再次查找。不过这一次如果找到的话,就需要在.c文件中添加#include << 文件名 >.h >的语句。
如果该结构还是不存在,请试着找找它是否是在哪个库中定义(请查看INSTALL或README文件以确定该软件使用了哪些库以及它们的版本)。如果该软件需要的版本不是系统上安装的那一个,就需要更新该库了。
如果依然不起作用,就要检查该程序是否真能在你的架构上运行了(某些程序还没有移植到Linux系统上),并检查你是否已经为自己的架构正确配置了该程序(比如在执行configure的时候)。
(3)parse error

这个问题解决起来较为复杂,因为编译器常会在真正出错的地方之后报错。有时候,它仅仅是由于某个数据类型未定义。如果碰上如下的错误信息:

main.c:1: parse error before `foo_t
main.c:1: warning: data definition has no type or storage class

那么很可能是foo_t类型未曾定义,解决方式同前一个问题类似。

(4)no space left on device

这个问题解决起来较为简单:磁盘上已经没有足够的空间从源文件生成二进制文件了。你可以通过释放安装目录所在分区的一些空间来解决(删除临时文件或源文件,卸载某些已经不用的程序)。

(5)/usr/bin/ld: cannot open -lglloq: No such file or directory

这表示ld程序无法找到某个库。为了包含某个库,ld将会搜索由-l< 库 >选项指定的库文件,相应文件为lib< 库 >.so。如果ld找不到,它将给出一条错误信息。要解决该问题,可以如下操作。

用locate命令检查该文件是否在硬盘上。通常,图形库在/usr/X11R6/lib目录中。比如:

# locate libglloq

如果上述查找没有结果,就使用find命令再次查找(比如find /usr -name "libglloq.so*")。如果还是找不到,就需要安装它了。

一旦找到了该库,请检查它是否能被ld访问。/etc/ld.so.conf文件指定了寻找这些库文件的目录,把该库的路径添加到该文件末尾(可能需要重启系统以使改动起作用)。也可以把该目录添加到环境变量LD_LIBRARY_PATH中。
如果还是不行,请用file命令检查该库文件是否为一个可执行文件。如果它是一个符号链接,请检查该链接完好且没有指向不存在的文件(可用nm libglloq.so检查)。而且,该库文件的权限可能是错误的(比如,如果不是root用户且该库文件不允许读)。
(6)glloq.c(.text+0x34): undefined reference to `glloq_init'

该问题是由于在编译的最后阶段某个符号找不到。通常,这是因为某个库出问题了。可能的问题如下。

首先,请查找该符号是否应该在某个库文件中。比如,如果该符号以gtk开头,它就应该属于gtk库。如果这个库能够很容易地找到,你可以用nm命令列出该库中的符号。比如:

# nm libglloq.so
0000000000109df0 d glloq_message_func
000000000010a984 b glloq_msg
0000000000008a58 t glloq_nearest_pow
0000000000109dd8 d glloq_free_list
0000000000109cf8 d glloq_mem_chunk

使用nm时添加-o选项可以分别在不同行中显示库中的名称,从而使搜索变得更为简单。假定我们要搜索符号bulgroz_max,可以:

# nm /usr/lib/lib*.so | grep bulgroz_max
# nm /usr/X11R6/lib/lib*.so | grep bulgroz_max
# nm /usr/local/lib/lib*.so | grep bulgroz_max
/usr/local/lib/libfrobnicate.so:000000000004d848 T bulgroz_max

可以看到该符号bulgroz_max定义于frobnicate库中(其名称前有一个大写字符T)。然后,你只需要编辑Makefile文件以在编译命令行中添加字符串-lfrobnicate(将其添加于定义LDFLAGS或LFGLAGS的那一行的末尾,或是在创建相应二进制文件的那一行处)。

编译中使用的库文件不是该软件需要的。请阅读该发布版的README或INSTALL文件,以查看需要哪个版本。
该发布版的目标文件没有全部正确链接,缺少了定义该函数的文件。请键入nm -o *.o以查看应该是哪个文件,然后将相应的.o文件添加到对应的编译命令行上。
出错的函数或变量可能并不存在,可以尝试删除它:编辑出问题的源文件(它的名字会出现于出错信息的开始处)。这可能会导致程序执行混乱,以及在启动时出现segfault(段错误)等错误。
(7)Segmentation fault (core dumped)

有时候,编译器立即挂起,并产生该错误信息。除了建议安装一个更新版本的编译器,没有更好的办法了。

(8)no space on /tmp

在不同的阶段,编译器需要临时工作空间,如果它不能申请到这些空间的话,它将出错。因此,你需要清理分区,不过请尽量小心,因为删除某些文件会导致某些正在执行的程序(X 服务器、管道等)挂起。你必需能够清楚知道自己在做什么!如果/tmp所在的分区并不仅仅包含该目录(比如根目录),请搜索并删除core文件。

(9)make/configure 死循环

在你的系统上,这常常只是一个时间上的问题。make确实需要了解计算机时间和它检查的文件的时间。它比较这两个时间,并根据结果确定某个目标是否已过时。

某些日期问题可能导致make不停地编译(或不停地递归编译某个子目录)。在这种情况下,touch(其作用是将有问题的文件的时间设定为当前时间)通常会解决该问题。比如:

# touch *

或者:

# find . | xargs touch

2.3.4 安装
既然编译已经完成,接下来就需要将编译后的文件复制到一个合适的位置(通常是在/usr/local的某个子目录中)。执行make install将完成该任务,安装所有需要的文件。

通常,在INSTALL或README文件中将描述该过程。不过有时候,开发人员会忘了提供这一信息。那时,我们就必须亲自安装所有东西了。

复制可执行文件(程序)到/usr/local/bin目录。
复制库文件(lib*.so文件)到/usr/local/lib目录。
复制头文件(*.h文件)到/usr/local/include目录。
复制数据文件通常到/usr/local/share。如果你不知道安装的过程,可以先试着不复制数据文件运行程序,然后就可以按照程序的提示将他们复制到正确的位置了(比如根据某个出错信息:Cannot open /usr/local/share/glloq/data.db)。
文档的安装有一点不同:man文件通常会被复制到某个/usr/local/man的子目录中,通常这些文件的格式为troff(或groff),且它们的扩展名是某个数字,它们的文件名是某个命令的名称(比如echo.1),根据其扩展名n将其复制到 /usr/local/man/man< n >目录;info文件复制到/usr/info或/usr/local/info目录。

相关文章
|
4天前
|
NoSQL Linux 测试技术
Redis的安装(Linux版)
Redis的安装(Linux版)
42 1
|
13天前
|
Linux C语言
Linux内核队列queue.h
Linux内核队列queue.h
|
14天前
|
缓存 Linux 测试技术
安装【银河麒麟V10】linux系统--并挂载镜像
安装【银河麒麟V10】linux系统--并挂载镜像
72 0
|
14天前
|
Linux C语言
linux yum安装ffmpeg 图文详解
linux yum安装ffmpeg 图文详解
34 0
|
14天前
|
NoSQL Linux Redis
linux 下和win下安装redis 并添加开机自启 图文详解
linux 下和win下安装redis 并添加开机自启 图文详解
17 0
|
14天前
|
Linux
linux yum 安装rar和unrar
linux yum 安装rar和unrar
47 0
|
1天前
|
Linux 开发工具 Android开发
Docker系列(1)安装Linux系统编译Android源码
Docker系列(1)安装Linux系统编译Android源码
3 0
|
1天前
|
Ubuntu Linux 开发工具
WSL2(3)安装Linux headers完美解决方案
WSL2(3)安装Linux headers完美解决方案
3 0
|
1天前
|
Linux 开发工具 C语言
Linux 安装 gcc 编译运行 C程序
Linux 安装 gcc 编译运行 C程序
10 0
|
1天前
|
Ubuntu Linux Python
Linux(15)Ubuntu安装ninja构建工具
Linux(15)Ubuntu安装ninja构建工具
8 0