openSUSE:Shared library packaging policy
通用打包指南 - 命名指南 - Spec 文件指南 - 补丁指南 - 安全性指南 - RPM Group 组指南
构建服务教学 - 技巧和花样 - 跨发行版打包 - Debian 打包指南 - 打包检查
桌面菜单分类 - 打包常用的 RPM 宏 - 小脚本片段 - SysVinit 脚本 - 源代码服务
OBS 打包互助问答 - 打包黑名单
构建服务教学 - 技巧和花样 - 跨发行版打包 - Debian 打包指南 - 打包检查
桌面菜单分类 - 打包常用的 RPM 宏 - 小脚本片段 - SysVinit 脚本 - 源代码服务
OBS 打包互助问答 - 打包黑名单
这个策略为打包共享库提供了命名的惯例。 这些共享库存在于 /%_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.$MAJOR 是 lib$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.rpm。libbar.so.3是PLONK 4.1 的一部分,那么它将被打包进libplonk41.rpm。libplonk41.rpm包含多个共享库,并且他们同步更新。
- lib$NAME$NUM.rpm包对应的开发文件被打包进-devel包中(不同版本的-devel包的解决方案详见(4a)和(4b))。这些开发文件包括lib*.so,lib*.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.6 和 ld-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个包,libcurl4,libcurl-devel,curl和curl-ca-bundle(CA的认证文件)。