Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1150936
  • 博文数量: 222
  • 博客积分: 5262
  • 博客等级: 大校
  • 技术积分: 3028
  • 用 户 组: 普通用户
  • 注册时间: 2009-11-22 19:10
文章分类

全部博文(222)

文章存档

2012年(2)

2011年(192)

2010年(28)

分类: 嵌入式

2010-12-16 18:49:30

配置

仅从技术兴趣上看,作者提供源代码的目的在于移植该软件。为 UNIX?? 系统开发的自由软件能够在现存的 UNIX?? 系统上(无论是自由的还是私有的)几乎不加修改地使用。而这需要在编译这些软件之前对其进行配置。

有好几个配置系统。不过您得使用软件作者希望的那一个(有时可能有好几个)。一般,您可以:

  • 如果在该软件的主发布目录中存在一个称为 configure 的文件,您可以使用 AutoConf (参见“Autoconf”一节)。

  • 如果在该软件的主发布目录中存在一个称为 Imakefile 的文件,您可以使用 imake (参见“Imake”一节)。

  • 根据 INSTALL 文件(或 README 文件)内容提示运行一个 shell 脚本 (比如 install.sh)。

Autoconf

原则

AutoConf 用以正确配置软件。它创建编译需要的文件(比如 Makefile),且有时会直接修改源代码(比如使用 config.h.in 文件)。

AutoConf 的规则十分简单:

  • 该软件的程序员了解配置他的软件需要进行哪些测试(比如:“您使用哪一版本的库文件”)。他使用一种精确的语法将这些测试编写进 configure.in 文件。

  • 并且,他通过运行 AutoConfconfigure.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: error: Cannot find library guile (大部分 configure 脚本的出错信息都是如此)。

这表示 configure 脚本找不到某个库文件(例如上例中的 guile)。这是因为 configure 脚本在测试时会使用该库文件编译一个小测试程序,如果它不能成功编译该程序,就说明它也不能够编译该软件。所以它给出该出错信息。

  • 您可以在 config.log 文件尾部找到配置过程所执行的每一步骤,从中能够发现产生这一错误的原因。C 编译器的错误提示十分明了。这将帮您解决该问题。

  • 请检查提示的库文件是否已正确安装。如果没有,请(从源代码或已编译的二进制文件)安装之后再运行 configure。检查它是否已安装的有效方式是查找包含提示字符串的库文件,一般是 lib<名称>.so。比如,

    $ find / -name 'libguile*'
    

    或者:

    $ locate libguile
    
  • 请检查编译器是否能够访问该库文件:它是否在 /usr/lib/lib/usr/X11R6/lib (或是在由环境变量 LD_LIBRARY_PATH 指出的,详细解释参见“是什么原因它不起作用?”一节中的b)目录中。并键入 file libguile.so 以检查该文件是否为一库文件。

  • 请检查该库文件相应的头文件是否已安装于正确地方(通常是 /usr/include/usr/local/include/usr/X11R6/include)。如果您不知道您需要什么头文件,请检查您是否安装了所需库文件的相应开发版本(比如,对 libgtk+2.0 而言您安装了 libgtk+2.0-devel)。这一库文件的开发版本为使用该库进行编译的软件提供所需的“include”文件。

  • 请检查您拥有足够的磁盘空间(configure 脚本需要一些空间来保存中间文件)。请使用 df -h 来显示您系统中各分区的使用情况,并注意其中满的或即将满了的分区。

如果您不理解 config.log 文件中的错误信息,请在自由软件社区中提出您的问题(参见“技术支持”一节)。

而且,即使 configure 提示某个库文件不存在,也请检查一下它是否存在(比如,很可能会有某个非常奇怪的提示说您的系统中竟然没有 curses 库)。在那种情况下,很可能是 LD_LIBRARY_PATH 变量出错了!

Imake

imake 让您能够使用简单的规则生成 Makefile 文件以配置自由软件。这些规则指定需要编译哪些文件才能生成所需的二进制文件,而 imake 生成相应的 Makefile。可在 Imakefile 文件中指定这些规则。

有意思的是 imake 使用站点相关(架构相关)的信息。这非常适用于使用 X Window 系统 的应用程序。不过 imake 也用于许多其他的应用程序。

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

$ xmkmf -a
$ imake -DUseInstalled -I/usr/X11R6/lib/X11/config
$ make Makefiles

如果本机没有正确安装,请重新编译并安装 X11R6

各种 shell 脚本

请阅读 INSTALLREADME 文件以了解进一步信息。通常,您需要执行 install.shconfigure.sh 文件。然后,该安装脚本或是非交互的(它自己决定需要什么),或者会向您询问有关您系统的信息(比如是路径)。

如果您无法确定应该运行哪个文件,您可以(在 bash 下)键入 ./,并按 TAB 键(跳格键)两次。bash (在其默认配置下)会自动用该目录中的可执行文件来补全(亦即某个可执行配置脚本)。如果有好几个可执行文件,它将显示这些文件的列表。然后您只需选中正确的那个。

另一个特例是安装 perl 模块。这种模块的安装是通过执行一个以 perl 编写的配置脚本完成的。通常需要执行的命令为:

$ perl Makefile.PL

其他

某些自由软件组织混乱,特别是在其初期开发阶段(不过它们会提醒其用户!)。有时它们会要求您“手动”更改某些配置文件。通常,这些文件是 Makefile 文件(参见“Make”一节)和 config.h 文件(其名称是约定俗成的)。

我们建议不要采用这种维护方式,除非用户确实明白他们在作什么。这需要许多知识以及一些成功的动力,不过正所谓熟能生巧嘛。

编译

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

Make

自由软件社区中编译源代码最得宠的工具是 make。它具有两个优点:

  • 帮助开发者有效地管理项目的编译过程而节省了他们的时间。

  • 对于最终用户而言,他们只需键入几行命令就能够编译并安装软件,即使他们从来没有开发的经验。

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

规则

make 按照依赖性进行操作,因此为了编译某个二进制文件(“目标”)需要依次执行几个步骤(“依赖”)。例如,(假定)要创建 glloq 这个二进制文件,必需编译并链接 main.oinit.o 这两个目标文件(编译过程的中间文件)。而这两个目标文件同样也是目标,它们分别依赖于各自相应的源文件。

本文仅能对纷繁芜杂的 make 用法作一简略介绍。更详尽的文档,请参考使用 Make 管理项目,第二版,O'ReillyAndrew OramSteve Talbott 著。

开始执行!

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

  • 不加参数执行 make 表示仅编译程序,而不安装。

  • make install 编译程序(不是一定的),并随后将文件安装到文件系统的正确位置。某些文件常常无法正确安装(比如 maninfo),可能会需要用户自己手动复制。有时候,需要在子目录中再次执行 make install。通常这是由于包含了第三方开发的模块。

  • make clean 清除编译产生的所有临时文件,大多数情况下还会删除可执行文件。

第一个阶段是编译程序,因此请键入(假设的例子):

$ make
gcc -c glloq.c -o glloq.o
gcc -c init.c -o init.o
gcc -c main.c -o main.o
gcc -lgtk -lgdk -lglib -lXext -lX11 -lm glloq.o init.o main.o -o glloq

好,二进制文件已经正确编译完成。我们可以进入下一个阶段 -- 安装该发行版的文件了(二进制文件、数据文件等)。参见“安装”一节。

解释

如果您曾好奇地查看过 Makefile 文件,您会发现一些已知的命令(rmmvcp 等),以及一些诸如 $(CFLAGS) 的奇怪字符串。

它们是变量,通常它们在 Makefile 文件开始处被设定。然后,在它们出现的地方会被相应的值所取代。当您需要一直使用同样的编译选项时,这会非常有用。

例如,要使用 make all 在屏幕上显示字符串“foo”:

TEST = foo
all:
        echo $(TEST)

以下变量经常被设置:

  1. CC:编译器。通常为 cc,在大多数自由系统中是 gcc 的代名词。如果不能肯定,可将其设为 gcc

  2. LD:该程序用以确保编译的最后一个阶段(参见“编译的四个步骤”一节)。默认为 ld

  3. CFLAGS:它将在编译的第一个阶段为编译器提供额外的参数。其中有:

    • -I<路径>:通知编译器到哪里寻找额外的头文件(比如:-I/usr/X11R6/include 表示可以直接包含存放于 /usr/X11R6/include 中的头文件)。

    • -D<符号>:定义额外的符号,对依赖已定义的符号进行编译的程序(比如:当定义了 HAVE_STRING_H 后将使用 string.h 文件)很有用。

    编译的命令行常如:

    $(CC) $(CFLAGS) -c foo.c -o foo.o
    
  4. LDFLAGS (或 LFLAGS):在编译的最后阶段使用的参数。其中有:

    • -L<路径>:指定搜索库文件的额外路径(比如:-L/usr/X11R6/lib)。

    • -l<库>:指定在编译的最后阶段使用的额外库文件。

是什么原因它不起作用?

不要惊慌,谁都可能犯错。其中最常见的是:

  1. glloq.c:16: decl.h: No such file or directory

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

    • 请检查该头文件确实已经存在于以下某个目录中:/usr/include/usr/local/include/usr/X11R6/include 或它们的某个子目录。如果没有,请在整个磁盘上查找它(可用 findlocate)。如果还是没有,请检查您是否已经安装了该头文件对应的库了。您可以在 findlocate 命令各自的手册页面中分别找到它们的示例。

    • 请检查该头文件确实可以读取(键入 less <路径>/<文件>.h 来测试)。

    • 如果它在 /usr/local/include/usr/X11R6/include 目录中,您有时可能需要为您的编译器添加额外的参数。用您常用的文本编辑器(EmacsVi 等)打开对应的 Makefile (请注意,您得打开编译出错的那个目录中的文件[33])。查找到出错的那一行,并紧接着调用编译器(gcc,有时是 $(CC))的地方添加字符串 -I<路径>(其中<路径>是包含该头文件的路径)。如果您不清楚要把该选项加到哪里,请把它添加到文件开始处 CFLAGS=<什么什么的> 后面或是 CC=<什么什么的> 后面。

    • 再次运行 make,如果它还是不起作用,请检查这个选项(见前文)确实被添加于编译过程中出错的地方。

    • 如果还是不起作用,请向您周围的高手求助,或向自由软件社区求助(参见“技术支持”一节)。

  2. glloq.c:28: `struct foo' undeclared (first use this function)

    结构几乎是所有程序都会使用的一种特殊类型。系统在头文件中定义了许多。这表示该问题很可能是由于找不到头文件,或误用头文件造成。解决该问题的正确步骤是:

    • 试着查一查出问题的结构是否已由程序或系统定义。比如用 grep 命令查看该结构是否已经在某个头文件中定义了。

      例如,当您在该软件源程序的根目录中时:

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

      在屏幕上可能会出现许多行(例如,每一次某个函数使用该类型的结构定义一个实例)。如果它们存在,找到头文件中 grep 指出的那一行。

      结构的定义应该是:

      struct foo {
          <结构内容>
      };
      

      请检查它是否就是您所要的。如果是,则说明该头文件未包含于出错的 .c 文件中。有两个解决方法:

      • 在出错的 .c 开始处添加 #include "<文件名>.h" 一行。

      • 或者将该结构的定义复制-粘贴到该文件的开始处(不是很美观,不过至少能起作用)。

    • 如果没有,在系统头文件中(通常位于 /usr/include/usr/X11R6/include/usr/local/include)再次查找。不过这一次,您得添加 #include <<文件名>.h>

    • 如果该结构还是不存在,请试着找找它应该在哪个库(即保存许多函数的一个软件包)中定义(请查看 INSTALLREADME 文件以确定该程序使用哪些库以及它们的版本)。如果该程序需要的版本不是您系统上安装的那一个,您就需要更新该库了。

    • 如果依然不起作用,请检查该程序是否真能在您的架构上运行(某些程序还没有移植到 UNIX?? 系统上)。并请检查您是否已经为您的架构正确配置了该程序了(比如在执行 configure 的时候)。

  3. parse error

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

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

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

    注意

    如果我没有记错的话,老版本的 curses 库中就存在一个 parse error

  4. no space left on device

    这个问题解决起来较为简单:磁盘上已经没有足够的空间从源文件生成二进制文件了。您可以通过释放安装目录所在分区的一些空间来解决(删除临时文件或源文件,卸载某些您已经不用的程序)。如果您解压到 /tmp 而不是 /usr/local/src 目录中,它会阻止对 /tmp 分区不必要的填充。请检查磁盘上是否有 core 文件[34]。如果有,请删除它们;如果它们属于别的用户,请让他们删除这些文件。

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

    这表示 ld 程序(在编译的最后阶段由 gcc 调用)无法找到某个库。为了包含某个库,ld 将会搜索文件名由 -l<库> 选项指定的库文件。相应文件为 lib<库>.so。如果 ld 找不到,它将给出一条错误信息。要解决该问题,请如下操作:

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

      $ locate libglloq
      

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

    2. 一旦找到了该库,请检查它是否能被 ld 访问:/etc/ld.so.conf 文件指定寻找这些库文件的目录。把该库的路径添加到该文件末尾(您可能需要重启您的计算机以使改动起作用)。您也可以把该目录添加到环境变量 LD_LIBRARY_PATH 中。例如,如果要添加 /usr/X11R6/lib 目录,请键入:

      export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/X11R6/lib
      

      (如果您使用 bash 作为您的 shell)。

    3. 如果还是不行,请用 file 命令检查该库文件是否为一个可执行文件(或 ELF)。如果它是一个符号链接,请检查该链接完好且没有指向不存在的文件(例如,可用 nm libglloq.so 检查)。而且,该库文件的权限可能是错误的(例如,如果您不是 root 且该库文件不允许读)。

  6. glloq.c(.text+0x34): undefined reference to `glloq_init'

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

    • 首先,请查找该符号是否应该在某个库文件中。比如,如果该符号以 gtk 开头,它就应该属于 gtk 库。如果这个库能够容易地找到(frobnicate_foobar),您可以用 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:请将其添加于定义 LDFLAGSLFGLAGS (或至少 CC)的那一行的末尾,或是在创建相应二进制文件的那一行处。

    • 编译中使用的库文件不是该软件需要的。请阅读该发行版的 READMEINSTALL 文件,以查看需要哪个版本。

    • 该发行版的目标文件没有全部正确链接。缺少了定义该函数的文件。请键入 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
    
阅读(2530) | 评论(0) | 转发(0) |
0

上一篇:Arm移植之TinyX

下一篇: gdb常用命令

给主人留下些什么吧!~~