【译】GMP 编译及安装(GMP 6.2.1)

2,917 阅读12分钟

注:本文中的“构建”特指 build,“编译”指 compile

安装 GMP

本段原文链接: gmplib.org/manual/Inst…

GMP 项目使用基于 autoconf/automake/libtool 的配置系统进行配置。在 Unix 类系统中,可以用下面的命令完成基本的构建:

./configure
make

可以用下面的命令进行功能测试:

make check

可以用下面的命令来安装 GMP (默认安装至/usr/local):

make install

构建选项

本段原文链接: gmplib.org/manual/Buil…

所有常用的 autoconf 配置选项都是可用的,可以使用 ./configure --help 来查看这些选项。此外, INSTALL.autoconf 文件也包含了一些通用的安装信息。

工具

configure 需要多种 Unix 类系统提供的工具。对于非 Unix 类系统,请参阅特殊系统注意事项

如果不想用 configure 进行构建也许也是可行的,毕竟目录中包含所有的源代码。但那样你需要自己解决很多问题。

构建目录

如果想要在其他目录(而非源码目录)构建,要首先 cd 到那个目录中,然后使用绝对路径来指定调用 GMP 源码目录下的 configure ,例如:

cd /my/build/dir
/my/sources/gmp-6.2.1/configure

并非所有的 make 工具都包含了切换目录所需的特性(VPATH),例如 SunOS 和 Slowaris make 的 bug 会导致这些工具无法切换目录构建。请考虑换用 GNU make

--prefix--exec-prefix 选项

--prefix 选项可以为 GMP 指定安装目录。该选项的默认值为 /usr/local

--exec-prefix 选项指定架构相关文件(例如 libgmp.a )的安装位置。该选项可实现在隔离架构相关文件的前提下,共享架构无关的部分文件(如文档)。需要注意, gmp.h 定义了 libgmp 中一部分的编码,这导致该文件也是架构相关的,因此需要保证该文件在 $prefix/include$exec_prefix/include 中都可被编译器搜索到

--disable-shared--disable-static 选项

GMP 默认会编译静态和动态两种库,但我们可以手动禁用其中的某一种。共享库能帮助生成更小的可执行文件,可以让多个进程共享一份代码,但在某些 CPU 上可能会稍微减慢运行速度,并会在每次函数调用时都产生一点额外开销。

本地编译( Native Compilation ),--build=CPU-VENDOR-OS

对于普通的本地编译,可以使用 --build 选项来指定系统。默认情况下,./configure 会使用 ./config.guess 的输出(译注:config.guess 是一个 gmp 源码包中的工具,直接运行会得到一个代表着系统类型的文本输出。例如我的是 nehalem-pc-linux-gnu)。在某些系统中, config.guess 可以确定具体的 CPU 类型,但如果不能给出,就需要在配置时显式指定了。例如:

./configure --build=ultrasparc-sun-solaris2.7

在各种情况下, "OS" 部分都是必要的,因为这会影响 libtools 如何生成共享库。如果不知道该填什么,可以通过运行 ./config.guess 来获取该值。

交叉编译,--host=CPU-VENDOR-OS

在交叉编译时,使用 --build 指定编译机的系统,使用 --host 指定 GMP 库将要运行的目标系统。例如,使用 FreeBSD Athlon 系统编译 m68k 的 GNU/Linux 二进制文件,命令如下:

./configure --build=athlon-pc-freebsd35 --host=m68k-mac-linux-gnu

首先会使用 host 系统类型作为前缀(prefix)来寻找编译工具。例如这里会先尝试使用 m68k-mac-linux-gnu-ranlib ,然后再尝试用 ranlib。这能让交叉编译工具和本地(native)工具共存。--host 所指定的值会被用于所用编译工具的前缀,该值也可以是别名,例如 m68k-linux。但需要注意,并不是只有这一种方式能够设置所用的工具,也可以只设置一个包含了交叉编译工具(例如 cc 等)的 PATH 环境变量来设置所用工具。

为同一个 CPU 家族的不同 CPU 编译程序也是一种交叉编译。尽管这可能只需要在本地编译器中加一个特殊选项。在任何情况下,./configure 都会避免构建依赖于用于构建的系统的代码,这对于为较新的 CPU 创建二进制文件时很重要,因为这样的二进制程序可能不会需要在构建系统上运行。

在所有的情况下,编译器都能将一个标准的 C main 程序构建为二进制可执行文件(无论是什么格式)。即使组成 libgmp 的只有对象文件,./configure 还会出于某些目的进行链接测试,例如确定 host 系统上有哪些函数可用。

当前在进行交叉编译时,除非显式指定 --build 选项,否则 configure 都会给出一个警告:因为在 PATH 中只有一个交叉编译 cc 的情况下,只靠 PATH 可能无法准确推断用于构建的系统的类型。

注意,--target 选项不适用于 GMP 。该选项通常是构建编译工具时使用的:构建编译工具时,使用 --host 指定工具运行在哪里,使用 --target 指定工具生成的代码要运行在哪里。普通程序以及 GMP 这样的库,只需要 --host 选项指定将运行的环境即可。(过去某些版本的 GMP 中,曾误用过 --target 选项)。

CPU 类型

通常而言,如果想要让程序运行速度尽可能快,就应该为 GMP 配置具体的 CPU 类型。然而这可能会导致二进制文件无法运行在同一个 CPU 家族的早期版本上,在同一 CPU 家族中更旧或更新的 CPU 上也可能运行得更慢。最好的做法是,总为要运行 GMP 的设备类型构建专属的 GMP 。

下面是目前 GMP 支持的 CPU 。若想知道具体选用的代码和编译器选项,请查看 configure.ac

  • Alpha: ‘alpha’, ‘alphaev5’, ‘alphaev56’, ‘alphapca56’, ‘alphapca57’, ‘alphaev6’, ‘alphaev67’, ‘alphaev68’ ‘alphaev7’
  • Cray: ‘c90’, ‘j90’, ‘t90’, ‘sv1’
  • HPPA: ‘hppa1.0’, ‘hppa1.1’, ‘hppa2.0’, ‘hppa2.0n’, ‘hppa2.0w’, ‘hppa64’
  • IA-64: ‘ia64’, ‘itanium’, ‘itanium2’
  • MIPS: ‘mips’, ‘mips3’, ‘mips64’
  • Motorola: ‘m68k’, ‘m68000’, ‘m68010’, ‘m68020’, ‘m68030’, ‘m68040’, ‘m68060’, ‘m68302’, ‘m68360’, ‘m88k’, ‘m88110’
  • POWER: ‘power’, ‘power1’, ‘power2’, ‘power2sc’
  • PowerPC: ‘powerpc’, ‘powerpc64’, ‘powerpc401’, ‘powerpc403’, ‘powerpc405’, ‘powerpc505’, ‘powerpc601’, ‘powerpc602’, ‘powerpc603’, ‘powerpc603e’, ‘powerpc604’, ‘powerpc604e’, ‘powerpc620’, ‘powerpc630’, ‘powerpc740’, ‘powerpc7400’, ‘powerpc7450’, ‘powerpc750’, ‘powerpc801’, ‘powerpc821’, ‘powerpc823’, ‘powerpc860’, ‘powerpc970’
  • SPARC: ‘sparc’, ‘sparcv8’, ‘microsparc’, ‘supersparc’, ‘sparcv9’, ‘ultrasparc’, ‘ultrasparc2’, ‘ultrasparc2i’, ‘ultrasparc3’, ‘sparc64’
  • x86 family: ‘i386’, ‘i486’, ‘i586’, ‘pentium’, ‘pentiummmx’, ‘pentiumpro’, ‘pentium2’, ‘pentium3’, ‘pentium4’, ‘k6’, ‘k62’, ‘k63’, ‘athlon’, ‘amd64’, ‘viac3’, ‘viac32’
  • Other: ‘arm’, ‘sh’, ‘sh2’, ‘vax’,

没有列出的 CPU 都使用通用的 C 代码。

通用 C 构建

如果某些汇编代码会产生问题,或者有某些其他的需求,可以通过配置 --disable-assembly 来指定使用通用的 C 代码。

需要注意这可能会减慢运行速度,但应该会有更好的可移植性,让之前汇编代码无法运行的功能运行起来。

胖二进制(Fat binary), --enable-fat

使用 --enable-fat 可以在 x86 上编译“胖二进制”代码,这能让程序在运行时根据具体的 CPU 类型来选择优化的低层子程序( low level subroutines )。这会增大二进制文件的体积,换来在所有的 x86 芯片上都获得很好的性能表现。(该选项未来可能会支持更多的架构类型)

ABI

在某些系统中,GMP 支持多个 ABI (应用二进制接口,Application Binary Interfaces),即数据类型的大小和约定。GMP 会默认选择最佳的 ABI ,但你也可以手动指定,例如:

./configure --host=mips64-sgi-irix6 ABI=n32

请参阅ABI 和 ISA 了解更多可选值,及应用需要注意的相关事项

CC, CFLAGS

默认情况下,构建 GMP 所选用的 C 编译器是在几个候选项中选出来的,若有 gcc 可选,则优先使用 gcc 。可以通过 CC=whatever 来将某个具体要使用的编译器传给 ./configure

对于各种不同的系统,默认的编译器标志( flags )都是基于 CPU 和编译器设置的。可以通过 CFLAGS="-whatever" 来将值传给 ./configure 以设置特定的标志。

被使用的 CCCFLAGS 会在 ./configure 的过程中打印出来,也可以在生成的 Makefile 中找到。这是检查默认值并考虑修改这两个值的最简单方式。

注意,当为一个支持多 ABI 的系统手动指定 CCCFLAGS 时,需要显式指定 ABI=whatever 。因为 GMP 无法只通过标志来确定 ABI ,也就无法选择正确的汇编代码。

如果仅指定了 CC ,则会使用该编译器默认的 CFLAGS (如果 GMP 能够识别编译器的话)。例如指定 CC=gcc 就可以强制使用 GCC ,并使用默认的标志(以及默认 ABI )。

CPPFLAGS

任何类似于 -D 的定义标志或类似于 -I 的预处理器所需 include 标志,都需要在 CPPFLAGS 中进行设置,而非 CFLAGS 中。编译过程会使用到 CPPFLAGSCFLAGS 两个标志,但预处理中指挥使用 CPPFLAGS 标志。存在这个区别是因为,绝大多数预处理器都不会接收编译器会接收的标志。预处理在某些配置测试中是分开进行的。

CC_FOR_BUILD

某些构建时程序( build-time programs )会被编译并运行,以生成 host 相关的数据表( host-spcific data tables )。CC_FOR_BUILD 就是用于该用途的编译器。其不需要被设置为任何特定的 ABI 或模式,只要能生成可运行的可执行文件即可。该选项的默认值是使用 CC 给定的编译器,或例如 ccgcc 这样的工具。

这种编译器不需要额外的标志,因为通常只需要进行最简单的、类似于 cc foo.c 这样的使用就足够了。如果一定要加上某些特殊的选项,直接在 cc 名称后面加上即可,例如 CC_FOR_BUILD="cc -whatever"

C++ 支持,--enable-cxx

GMP 可以通过 --enable-cxx 来启用 C++ 支持,这种情况下会用到 C++ 编译器。只有在能找到编译器的情况下,才能用 --enable-cxx=detect 来启用 C++ 支持。C++ 支持由 libgmpxx.la 库和 gmpxx.h 头文件组成(参阅 头文件和库

为 C++ 支持提供一个专用的 libgmpxx.la 而不是将 C++ 对象加入到 libgmp.la ,是为了确保动态链接的 C 程序不会因为依赖 C++ 标准库的代码而变臃肿,并避免出现在链接纯 C 程序时用到 C++ 编译器的情况。

libgmpxx.la 会使用一些 libgmp.la 中的内部逻辑,并只能和相同 GMP 版本的 libgmp.la 协同使用。未来对这些内部逻辑的修改也可能会包含重命名,因此造成的匹配错误可能会带来符号解析失败的问题,而不会有其他诡异的错误行为。

通常来说,libgmpxx.la 只能由构建它所用的 C++ 编译器使用,因为不同编译器的名称修改和运行时支持通常是不兼容的。

CXX, CXXFLAGS

当启用 C++ 支持时,可以通过CXX, CXXFLAGS来设置 C++ 编译器以及相应的标志。默认的 CXX 是一个候选编译器列表中的第一个,当 g++ 可用时会优先使用 g++CXXFLAGS 会默认尝试使用 CFLAGS、去掉 -gCFLAGS ,对于 g++ 会使用 -g -O2-O2,对于其他编译器会使用 -g 或不使用任何标志。当同时使用 gccg++ 时,可以尝试只使用 CFLAGS,因为 gcc 可用的标志通常也可以直接用于 g++

保持 C 和 C++ 编译器相匹配是很重要的,因这意味着它们的启动和运行时支持程序相兼容,它们生成的代码也有相同的 ABI (如果系统支持多个 ABI 的话)。./configure 目前还无法很好地检查这些东西,因此GMP默认启用 disable-cxx 选项,以避免由编译器不匹配造成的构建失败问题。该情况或许能在未来有所改变。

此外,不建议直接将 CXX 设置为 CC 。虽然 gcc 能将 foo.cc 识别为 C++ 代码,但只有 g++ 能够在用 C++ 对象文件编译可执行文件和动态库时,以正确的方式启动链接器。

临时内存,--enable-alloca=<choice>

GMP 使用下面三个方法来分配临时工作区。例如,可以使用 --enable-alloca=malloc-reentrant 来选择具体要用的方法为 malloc-reentrant

  • alloca - C 库或编译器内建;
  • malloc-reentrant - 堆, 通过可重入方式;
  • malloc-notreentrant - 堆,使用全局变量。

为了方便,还可以使用下面这些选项。--disable-alloca 等同于 no

  • yes - 等同于 alloca
  • no - 等同于 malloc-reentrant
  • reentrant - 如果可用,值为 alloca ,否则是 malloc-reentrant 。该项是默认值;
  • notreentrant - 如果可用,值为 alloca,否则是 malloc-notreentrant

alloca 可重入而且速度快,是推荐选项。该选项会在栈上分配较小的块。对于大的块还是会使用 malloc-reentrant

malloc-reentrant 是可重入且线程安全的。但 malloc-notreentrant 的速度更快,在不需要可重入特性的时候应优先选用;

两个内存分配方法实际上使用由 mp_set_memory_functions 所选择的内存分配函数,默认是 malloc 等函数。详情请参阅 自定义分配

还有一个 --enable-alloca=debug 选项可用,其能帮助调试内存相关问题(详情参阅 调试

FFT 乘法, --disable-fft

默认乘法是用 Karatsuba, 3-way Toom, high degree Toom 以及 Fermat FFT。FFT 仅用于大和超大操作数,如有需要可以禁用以减小代码体积。

断言检查( Assertion Checking ),--enable-assert

该选项可启用一些库中的一致性检查。该选项用于调试,详情参阅 调试

执行分析( Execution Profiling ),--enable-profiling=prof/gprof/instrument

启用不同种类的分析支持,详情参阅 分析

MPN_PATH

每个 MPN 子程序提供了不同版本的汇编代码。对于特定的 CPU ,会在预定的 PATH 中搜索每个 MPN 的最佳版本代码。例如 sparcv8

MPN_PATH="sparc32/v8 sparc32 generic"

这意味着会最先找 v8 代码,然后是 sparc32(即 v7),最后返回到通用 C 。有相关知识和特殊需求的用户可以指定一个不同的 path。通常完全没必要修改该值。