静态库文件是使用ar(1) 实用程序由预编译的目标文件(.o 文件)生成的。
链接程序从库中提取在当前链接的程序内引用了其入口点的任何元素,如子程序、入口名或 COMMON 子程序中已初始化的 BLOCKDATA 块。这些提取出来的元素(例程)会被永久绑定到链接程序生成的 a.out 可执行文件中。
与动态情况相比,关于静态库和链接,有三个主要问题需要谨记:
静态库更加自主,但适应能力较差。
如果以静态方式绑定 a.out 可执行文件,它所需的库例程会变成可执行二进制文件的一部分。但是,如果需要更新绑定到 a.out 可执行文件中的静态库例程,则必须重新链接并重新生成整个 a.out 文件以便利用已更新的库。对于动态库,库并不是 a.out 文件的一部分,并且链接是在运行时完成的。要利用已更新的动态库,只需将新库安装在系统中即可。
静态库中的“元素”是单独的编译单元,即 .o 文件。
由于单个编译单元(源文件)可以包含多个子程序,因此这些例程在一起编译时会变成静态库中的单一模块。这就意味着会将编译单元中的所有例程一起装入 a.out 可执行文件中,即使实际只调用了那些子程序中的一个。通过优化库例程分发到可编译源文件中的方式,可以改善这种情况。(尽管如此,只有程序实际引用的那些库模块才会被装入可执行文件。)
链接静态库时,顺序很重要。
链接程序按输入文件在命令行上出现的顺序(从左至右)对其进行处理。当链接程序决定是否从库中加载某一元素时,其决定取决于它已经处理的库元素。该顺序不仅依赖于元素在库文件中的出现顺序,而且还依赖于编译命令行中指定库的顺序。
示例:如果 Fortran 程序在两个文件(main.f 和 crunch.f)中,并且只有后者访问某个库,则在 crunch.f 或 crunch.o 之前引用该库是错误的:
demo% f95 main.f -lmylibrary crunch.f -o myprog |
(不正确)
demo% f95 main.f crunch.f -lmylibrary -o myprog |
(正确)
假设您可以将程序中的所有例程分布在一组源文件中,同时假定这些文件全部包含在子目录 test_lib/ 中。
进一步假定这些文件是以这样一种方式组织的:它们每一个都只包含一个用户程序将会调用的主要子程序,同时还包含该子程序可能会调用的任何“帮助程序”例程,但这些例程不会从库中的任何其他例程中调用。另外,从一个以上库例程中调用的任何帮助程序例程均被集合到单个源文件中。这样就给出了一个组织得非常合理的源文件及目标文件集。
假定每个源文件的名称均取自文件中第一个例程的名称,在多数情况下,该例程是库中的主要文件之一:
demo% cd test_lib demo% ls total 14 2 dropx.f 2 evalx.f 2 markx.f 2 delte.f 2 etc.f 2 linkz.f 2 point.f |
更低级的“帮助程序”例程被集中到文件 etc.f 中。其他文件可以包含一个或多个子程序。
首先,使用 -c 选项编译每一个库源文件,生成相应的可重定位 .o 文件:
demo% f95 -c *.f demo% ls total 42 2 dropx.f 4 etc.o 2 linkz.f 4 markx.o 2 delte.f 4 dropx.o 2 evalx.f 4 linkz.o 2 point.f 4 delte.o 2 etc.f 4 evalx.o 2 markx.f 4 point.o demo% |
现在,使用 ar 创建静态库 testlib.a:
demo% ar cr testlib.a *.o |
要使用该库,可在编译命令中包括此库文件,或者使用 -l 和 -L 编译选项。以下示例直接使用 .a 文件:
demo% cat trylib.f C program to test testlib routines x=21.998 call evalx(x) call point(x) print*, ’value ’,x end demo% f95 -o trylib trylib.f test_lib/testlib.a demo% |
注意,主程序只调用库中的两个例程。您可以验证并未将库中未调用的例程装入可执行文件,方法是查找用 nm 显示的可执行文件的名称列表中是否有这些例程。
demo% nm trylib | grep FUNC | grep point [146] | 70016| 152|FUNC |GLOB |0 |8 |point_ demo% nm trylib | grep FUNC | grep evalx [165] | 69848| 152|FUNC |GLOB |0 |8 |evalx_ demo% nm trylib | grep FUNC | grep delte demo% nm trylib | grep FUNC | grep markx demo% ..etc |
在上述示例中,grep 只在名称列表中查找与实际调用的那些库例程相应的项。
引用库的另一方法是通过 -llibrary 和 -Lpath 选项。这里,必须更改库的名称以符合 libname.a 惯例:
demo% mv test_lib/testlib.a test_lib/libtestlib.a demo% f95 -o trylib trylib.f -Ltest_lib -ltestlib |
-llibrary 和 -Lpath 选项与安装在系统公共访问目录(如 /usr/local/lib)中的库一起使用,以便其他用户可以引用它。例如,假如将 libtestlib.a 置于 /usr/local/lib 中,可以通知其他用户使用以下命令编译:
demo% f95 -o myprog myprog.f -L/usr/local/lib -ltestlib |
如果仅有几个元素需要重新编译,没有必要重新编译整个库。ar 的 -r 选项允许替换静态库中的个别元素。
示例:重新编译并替换静态库中的单个例程:
demo% f95 -c point.f demo% ar -r testlib.a point.o |
要在 ar 正在生成静态库时对其中的元素进行排序,请使用命令 lorder(1) 和 tsort(1):
demo% ar -cr mylib.a ’lorder exg.o fofx.o diffz.o | tsort’ |