openSUSE:Shared library packaging policy

跳转至: 导航, 搜索
这个策略为打包共享库提供了命名的惯例。 这些共享库存在于 /%_lib 或者 /usr/%_lib (%_libdir)。其基本的原则是允许不同版本的共享库可以在不引起RPM冲突的前提下共存。 例如 libfoo1 and libfoo2 在不会引起RPM冲突的前提下同时安装到系统中。

基本原则

这个方案的目的是让具有相同的基础名字(base name)不同版本的共享库可以被同时安装和使用。名字中包含一些单向增长的数字作为区别字符,这样可以让系统避免升级混乱。名字结构的严格限制有利于使用者写一些快速的工具。

来自不同的libfoo*.rpm包的文件不能冲突。因此在所有的libfoo*.rpm包中,只可以有lib*.so.*文件。为了确保任何一个共享库都在控制范围内,我们不允许任何一个库所在的包不遵守这些规则。

实际上,这些规则会把共享库和其它文件分开成两个部分。任何一个rpm不能同时包含来自这两个部分的文件。

定义

  • lib$NAME.so.* — 共享库 程序依赖它们运行,一般包含两个文件:
    lib$NAME.so.$MAJOR
    lib$NAME.so.$MAJOR.$MINOR.$PATCHLEVEL,
    其中 (在linux系统中)lib$NAME.so.$MAJORlib$NAME.so.$MAJOR.$MINOR.$PATCHLEVEL.
  • SONAME — 动态链接器使用这个名字来引用共享库。 (通过 readelf -d libfoo.so.1 | grep SONAME.) 它通常匹配那个短一点符号链接的名字,lib$NAME.so.$MAJOR
  • lib$NAME.so — 链接器(ld)用它来链接到可执行程序。它通常是一个符号链接,链接到上面提到的共享库文件。
  • lib$NAME.a — 静态链接库,链接器(ld)用它来静态地链接可执行程序。
  • lib$NAME.la — libtool的配置文件。在静态链接lib$NAME时使用,在用libtool dynamic loader ltdl做动态链接的时候使用。

软件包的名字

  • 目录 /%_lib or /usr/%_lib (%_libdir) 下的库文件所在的rpm包的名字格式的结构是 "lib" + $NAME + $NUM
    • $NAME 是SONAME中的去掉前缀"lib"和后缀".so.*"之后剩下的部分。 (libfoo.so.1 -> foo)
    • 如果 $NAME 以数字结尾,那么在 $NAME$NUM 之间需要一个减号。例如有 libfoo1-0, 而不是 libfoo10.
    • 如果$NAME中包含句点。例如foo1.99那么句点需要用下划线替换。那么结果就是foo1_99
  • 目录/%_lib或者/usr/%_lib下的库文件都需要有对应的开发库。而这些开发库文件被打包在名为-devel的软件包中。唯一的例外是如果这些库只被一个包专用参见(1).
  • 名为lib$NAME$NUM这样的软件包只能包含lib*.so.*(不能包含头文件*.so, config文件, 和其它文件)。例外情况 参见(3)
  • $NUM只能包含十进制的数字和下划线。
  • $NUM实际就是SONAME中的数字部分,但是如果其中有句点需要用下划线替代。
    • 一般情况下SONAME中的数字就是这个共享库的主版本号MAJOR:比如libfoo.so.1 => NUM=1
    • 还是有一些库使用了一些郁闷的版本号,例如openssl的。它滥用了接口的版本来编码库版本,所以有libopenssl.so.0.9.8 => NUM=0_9_8
  • lib*开头,以$NUM结尾的名字叫无后缀的软件包名字。
    • 常见的后缀包括-devel或者-debuginfo
  • 一般情况下以-devel结尾的包都应忽略掉$NUM因为不同版本的-devel会因为包含共同的头文件而冲突。解决这类情况详见解决这类情况参见(4a) and (4b)

软件包的内容

  • lib$NAME$NUM.rpm 只包含一个名为lib$NAME.so.*或者包含多个共享库。它包含文档或者license文件,除非license要求那样做。
  • lib$NAME$NUM.rpm 可以包含多个共享库,只要这些共享库的版本保持同步更新。
    • 如果 这些库中间的某个库不依赖所有或大部分的其它的库,那么最好不要把他们放在同一个rpm中。但考虑到安装文件的大小。例外也是允许的。
    • 例子:libfoo.so.1被打包进libfoo1.rpmlibbar.so.3是PLONK 4.1 的一部分,那么它将被打包进libplonk41.rpm。libplonk41.rpm包含多个共享库,并且他们同步更新。
  • lib$NAME$NUM.rpm包对应的开发文件被打包进-devel包中(不同版本的-devel包的解决方案详见(4a)和(4b))。这些开发文件包括lib*.solib*.la和所有的头文件。偶尔这些开发文件也会被打包进$NAME.rpm,但同时也会附带其他的工具和文档。把license文件打把进-devel包是比较恰当的。
  • lib$NAME$NUM.rpm不应该被手动安装。这些包的正确分组是System/Libraries

最佳经验

下面是一些通用的打包指引:

  • 避免打包静态库文件。你应该使用configure 的选项--disable-static,或者至少在%makeinstall之后删除静态库。
  • 如果一定要打包静态库,那么编译静态库时不应该-fPIC
  • 如果打包静态库但是不打包共享库,那么编译静态库时必须-fPIC,需要使用--with-pic选项如果可用。
  • 避免打包libtool的配置文件(.la)。通常,在不打包静态库,并且共享库被放在系统目录(比如,%_lib, /usr/%_lib)中。但如果共享库被放在某个其他的目录下,比如/opt/package/lib/libfoo.so.7,那么libfoo.la文件是需要的,否则那些使用libtool来链接到libfoo.so.7的程序会找不到这确的路径。
  • 如果有疑问,可以讨论。
  • 共享库通常不可单独运行(例外:libc.so.6ld-linux.so.2)。但是它们一般都有+x权限位。

例外

  • (1) 那些只有某个主程序使用共享库,必须被打包进主rpm,不需要再单独打包到lib$NAME$NUM中。并且要确保如下要求:
    • 这些共享库要被放在/usr%_lib/的某个子目录中,其名字需要和主包的名字有关。
    • 没有对应的devel文件,没有*.a*.so文件,没有同文件需要被打包到lib$NAME-devel中。
  • (2) 不受这个策略约束的包如下(新增的包需要被讨论通过):
    • glibc
    • pam
  • (3) 如果一个共享库包lib$NAME$NUM必须包含其他文件,那么他们的名字必须明确,不能有歧义,要么将其放在不同版本的目录中,要么将其名字版本化。
  • (4a) 如果在同一个源中,同一个库的不同版本都可用,那么-devel的包需要加后缀来避免同名包。并且恰当的冲突关系需要加进来。
  • (4b) 如果同一个库的不同版本可以同时安装到系统中。那么对应的-devel需要加数字(通常和NUM相同)后缀以区分。所以某个-devel包可能被命名为lib$NAME$NUM-devel.

提示

当你试图遵守这个策略时,应该记得如下提示:

  • 没有要求一个-devel包不能为多个共享库的包提供开发文件。
  • 二进制包不一定要匹配源码包的名字。事实上,你不应该改变源码包的名字去对应不同的版本。因为我们倾向在源里只包含某个源码包的一个版本。
  • 共享库的被依赖关系可以通过rpm自动创建,或者根据-devel包而定,某个devel包需要明确的指出它需要的共享库的版本。
  • 这些策略只针对共享库的包和那些存储在/%_lib或者/usr/%_lib下的库文件有用。

例子

一个源码rpm包产生一个单独的共享库和开发包是最简单的例子。共享库zlib就是这样的一个例子。最终的二进制包应该被命名为libz1和libz-devel,它们包含如下内容,

libz1:

/lib/libz.so.1
/lib/libz.so.1.2.3

libz-devel:

/usr/lib/libz.so
/usr/lib/libz.a
/usr/include/zlib.h
/usr/share/man/man3/zlib.3.gz
/usr/share/doc/packages/zlib/algorithm.txt

一个稍复杂的例子是,除了二进制文件和开发文件还有可执行文件和文档。比如bzip2就属于这类包。这里会有4个包。

libbz2-1:

/lib/libbz2.so.1
/lib/libbz2.so.1.0.0

libbz2-devel:

/usr/include/bzlib.h
/usr/lib/libbz2.so
/usr/lib/libbz2.a

bzip2:

/usr/bin/bzip2
/usr/share/man/man1/bzip2.1.gz

bzip2-doc:

/usr/share/doc/packages/bzip2-doc/manual.ps.gz

这里的文档可以被合并到二进制文档包bzip2或者是开发包libbz2-devel中。

另一个例子是,这类库的不同版本依赖的共同的配置文件或者数据文件。这种情况那些配置文件和数据文件应该被单独打包。并且共享库需要依赖他们。curl就属于这种情况,它包括4个包,libcurl4libcurl-develcurlcurl-ca-bundle(CA的认证文件)。