LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
12
返回列表 发新帖
楼主: lewbing

[已解决]请教程序编译打包的问题

[复制链接]
 楼主| 发表于 2007-8-20 18:39:22 | 显示全部楼层
Post by manphiz
使用动态链接的方式是绝大部分发行版的默认方式。比如 Debian 的发行,就要求程序在万不得已的情况下必须动态连接系统中的库(所以你很难在 Debian 仓库中发现使用静态连接的库)。当前绝大部分库都基本保证 ABI 的兼容,因此链接静态库在大部分情况下不是必要的。如果出现 ABI 变化,也最好在代码中进行处理,或者指定库版本,而尽量不要直接采用动态连接。

Open Source 最核心的意义就是能利用别人的成果。如果每个人都自己去实现相同的功能,那会导致大量的重复劳动。

再提一点: 商业程序在使用 LGPL 协议的库时必须使用动态连接的方式。使用静态方式是违反 LGPL 协议的,因为 LGPL 要求静态链接的程序必须公开源码。


这是我在查找什么是ABI时找到的, 谢谢你又提供了一个新名词.
比如 libstdc++.so.5 和 libstdc++.so.6 API 兼容, 但 ABI 不兼容, 一般的商业软件, Realplayer, Adobe Reader 都是用libstdc++.so.5 的 ABI , 但你自己不能重新编译, 所以必须要装 libstdc++.so.5 .
如果你有 Realplayer 的源码 ( Helix ), 因为 C++ 的 API 是兼容的, 就可以用 libstdc++.so.6 的 ABI 编译出用 libstdc++.so.6 的.

同样, 我也摘录一下什么是ABI和API.
ABI: Application Binary Interface, 相容的两个程序就是二进制兼容的
API: Application Programming Interface, 两个程序的ABI不同, 但API兼容, 用相同的ABI编译另一个就行了.

我理解吧, ABI兼容, 就是两个新旧版本的两个库版本可以兼容, 但是这样会有问题哦! 如: 现在我假如libstdc++5和libstdc++4 ABI兼容, 那当一个使用libstdc++4编译的软件时, 没有libstdc++4的库, 它肯定会报错. 如果你知道它们是ABI兼容的, 将libstdc++5做一个link到libstdc++4了, 好你的程序运行了, 但这样就会让人怀疑它的兼容性了. 而且这样, 肯定让企业用户觉得你的程序有缺陷.
API兼容, 怪怪的, 还不太理解, 还是我摘录的原话有些问题? "既然两个程序API兼容了, 用相同的ABI编译另一个程序就可以了." 这样的关系, 好怪哦? 稍后再查找一下API兼容是什么意思!

呵呵, 以前我对LGPL协议的印象是可以静态和动态包含的, 这次看到你的解答, 我也重新查看了一下, 的确只能动态包含. 如果静态包含, 要么开放包含的库; 要么放弃使用; 要么联系作者,让他授权你静态包含;要么就不用它了.
如果是这样的话, linux一些商业程序, 你就不得不动态包含一些库了. 我比较倾向于使用BSD协议, 它是对商业友好的, 又不失知识的共享.

linux的新旧版本库关系处理的非常好, 如同时安装libstdc++4和libstdc++5, 系统没有任何问题. 如果有个信赖不同的版本的库, 好像现在产生数据冗余是最好的解决办法. 我用过的只有windows, linux操作系统, 这两个操作系系统解决依赖问题都是同时存在新旧版本的库, 不知道MacOS是怎样解决的. 有没有更好的方法呢?
老是让新版本的库去兼容旧版本的库, 如果新版本库更新较多, 再去兼容旧版本的库, 周而复始, 最终肯定导致库很庞大. 如果像Qt4一样, 不是去完全兼容Qt3, 做一个Qt3到Qt4的代码转换工具较明智. 这样的例子也在MacOS是存在过, 记得哪一年的MacOS更新较多, 完全不兼容旧版本.
不知道这个问题怎么解决, 但这个问题也扯的有些远了, 谈到了软件最初的设计问题, 又要牵涉到软件工程,软件设计的问题了. 设计一个有先见之明的类库确实难啊(同时兼容历史的库版本更新)!
回复 支持 反对

使用道具 举报

 楼主| 发表于 2007-8-20 19:29:59 | 显示全部楼层
Post by jetking
"如果是嵌入式程序呢? 那些静态库不是挺占用空间吗?"
我也在想这个问题,其实对于嵌入式程序来说,静态编译是个好主意。
尽管动态编译的应用程序小,但是共享库一样要占用空间;对于通用系统来说,共享库可以有效减小尺寸,因为有许多程序可以使用同一个共享库而如果静态编译的话会很大;但是这种情况在嵌入式系统中基本不存在,应用程序是定制的,静态编译的话,对于制作嵌入式系统也比较方便,不用去管那么多的共享库了,只要一个应用程序就好。

sorry,审错题,把“静态库”理解成了“静态编译的程序”,嵌入式下主机上有静态库就行了,目标机上就不需要了呀


我看过这样一句话, 如果是只有一个程序依赖的库, 最好采用静态链接. 如果是很多程序会依赖的库, 还是用动态链接吧. 这是适会于开源软件的设计, 商业软件呢? 呵呵, 协议的限制, glibc, libstdc++等等开源库, 只有使用动态链接了.
回复 支持 反对

使用道具 举报

发表于 2007-8-20 21:02:00 | 显示全部楼层
从程序运行的角度讲,静态库是不必要的,因此如果想要打包,那么只是针对动态库进行打包。一个程序,它运行时加载的动态库并不完全是在编译时决定的,运行时系统按照指定的路径顺序进行查找,找到了名字符合的就会使用,而不管其版本。举一个现实中的例子,这两天编译一个软件,它要求的 freetype 库的版本是一个较低的版本,用高版本的库反而无法编译。我的系统中原配的 freetype 装在 /usr/lib 目录下,而我为了装此软件而编译的 freetype 库装在 /usr/local/lib 下,这样二者不会互相覆盖,我要编译的程序也可以成功运行;但是有一天我想运行系统自带的 The GIMP,于是问题出现了,它说我的 freetype 版本过低,而拒绝运行。我的电脑中并不是没有高版本的 freetype,但是从查找顺序上来说,低版本的更优先,所以导致了冲突。这只是一种情况,也许还有别的可能
回复 支持 反对

使用道具 举报

发表于 2007-8-20 21:43:15 | 显示全部楼层
另外,我所说的版本系列较少的是操作系统,而不是应用程序。如果你只打算支持一个操作系统的有限个版本,那么你尽可以针对这几个版本进行测试,确保按照你的打包方式可以正常工作,但是对于 Linux 来说,应用程序应该可以运行在所有已知或者未知的发行版上,这时我们面对的是一个无穷的集合,你可以保证你的安装包兼容了 N 个发行版,但你不能保证你可以兼容第 N+1 个。

Debian 或者类似的提供包管理器的发行版属于一种微妙的情况。用户可以在无数这样的带有包管理器的发行版中选择一个,但是他一旦选择了一个后,他所安装或打算安装的软件所面对的发行版就只有一个了,这样,包管理器可以保证他所提供的包对自身的完美兼容,但没有哪个包管理器会说我的包可以在其他的发行版上正常使用。

那么软件提供者呢?他所面对的是一个特定的发行版还是所有的发行版?这是必须要考虑的问题。大家在这里讨论的目的就是想找到一种通用的二进制分发方式,那么当然就是要讨论针对所有发行版的情况。上面我提到一种多版本运行库不兼容的情况。有若干解决这种不兼容的方法,比如:1. 把软件需要的运行库放在一个私有的目录下,然后在程序运行时通过指定库路径的方式来寻找我们依赖的库,这可以保证别的程序不会找到我们的库,也不会冲突。但是既然我们的库不打算给其他的程序用,其意义何在?倒不如直接静态连接或静态编译的好。2. 软件编译的时候不是让它依赖于 libxx.so 这样的库,而是让它依赖于 libxx.so.1 这样的带有版本号的库,这样我们在提供库的时候只提供这一个文件,这样对于依赖 libxx.so 的程序我们假定它们不会再产生干扰(这一点我不确定),但是这样就万无一失了吗?如果两个采用私有库的程序 A 和 B 都提供了 libxx.so.1,那么 A 在安装的时候提供了一份,B 在安装的时候,如果它把库装在 A 相同的目录下,这没什么,但是假如然后我们又删除了 B 呢?B 的卸载程序当然会把 libxx.so.1 也删掉,于是 A 完蛋了。假如 B 的安装程序够智能,它在安装的时候发现已经有人提供了 lib.so.1,于是不在重复安装,并记录这一信息,然后在卸载时不去删除它,那么好,A 还活着。可是假如 我们先删除的是 A 呢?A 是先安装的,他在安装时无法知道 B 也会被安装,不会知道 B 会提供和他相同的库,于是在卸载的时候当然要删掉 libxx.so.1,于是好心的 B 被干掉了。要想假如这种直接的冲突,必须要由系统或所有的安装程序达成一个约定,他提供对安装的库的计数。遗憾的是,现在的 linux 没有,也不太可能,提供这种计数,而指望所有的安装程序还维护这样的计数,短期内也是不可能的。

所以,二进制分发有着极大的限制,尤其是在 Linux 这样的开放的操作系统中。
回复 支持 反对

使用道具 举报

发表于 2007-8-20 21:54:22 | 显示全部楼层
我再来说一下个人对嵌入应用的特点的理解。

PC 上的应用程序为什么不要全部静态编译呢?因为那样系统的冗余度就太高了。一部分代码,如果多个程序共享,那么把这部分代码单独编译成一个库,大家都可以调用,总体积就变小了。

但是嵌入式应用的区别就在于,有可能没有“多个”程序来共享代码。对于面向低端的嵌入式应用来说,一个库能够被一两个程序使用就不错了,而且它们也不见得使用这个库的所有接口,可能只使用了一两个函数,这种情况下,静态编译反而可以使得总体积变小。但是对于面向高端的嵌入式应用来说,比如 PDA,其实和 PC 没有太大的区别,功能多,软件多,适当的采用共享库会有效地减小系统的体积。

所以桌面应用或是嵌入式应用,这其中并没有一条清晰的界限。叫什么不重要,是具体的应用环境决定了分发策略。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2007-8-22 07:09:16 | 显示全部楼层
Post by remote fish

...
我的电脑中并不是没有高版本的 freetype,但是从查找顺序上来说,低版本的更优先,所以导致了冲突。这只是一种情况,也许还有别的可能

我觉得吧, 可能是查找顺序有区别, 如LD_LIBRARY_PATH=/lib:/usr/lib:/usr/local/lib. 只不过它们是由系统默认查找的, 当然你也可以改更 LD_LIBRARY_PATH=/lib:/usr/local/lib:/usr/lib 测试一下它, 看是不是这样呢? PATH搜索的路径是/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin:/usr/local/sbin, 我猜想, LD_LIBRARY_PATH搜索方式与它类似, 但由于库的几个扫描处是由系统默认的, 默认没有在/etc/profile中更改它.

Post by remote fish

...
比如:1. 把软件需要的运行库放在一个私有的目录下,然后在程序运行时通过指定库路径的方式来寻找我们依赖的库,这可以保证别的程序不会找到我们的库,也不会冲突。但是既然我们的
库不打算给其他的程序用,其意义何在?倒不如直接静态连接或静态编译的好。
....

其它我一直在考虑, 把现在的linux程序路径给更改一下. 这不是从win下带来的习惯, 而是觉得这样组织上会更好些. 如: 建一个专用的目录保存所有的安装程序, 所有的程序安装在此目录时都新建一个以安装程序命名的文件夹来管理, 这就是完全私有化了. 也是的, 这样以后, 它的库就不能被别人使用了, 除非用一些其它的机制, 或是使用LD_LIBRARY_PATH指定(太麻烦了).
我现在就不太喜欢linux程序的组织方式, 我一些自己编译的程序, 我都是放在我自己的目录, 如/opt/mysoftwares, 将一些自己编译的程序和一些可以单独运行的程序, 像qt4, eclipse, mldonkey, doxygen, boost, ace等等, 全部放在下面, 我建一个脚本, 加上需要的参数, 在不同的shell启动脚本上加载. 我觉得这样还不错, 嗯, 但是有一些程序的资源文件需要在/usr/share, /usr/local/share目录下, 如果有一个指定share目录的环境变量的话, 我会把更多的程序给私有化, 再把它的一些目录加入到环境变量中. 如PATH,C_INCLUDE,CPP_INCLUDE, LD_LIBRARY_PATH, SHARE_PATH(假如有). 是现在没有时间, 还有对linux系统编程不太熟悉, 如果熟悉了, 我会给它加上一个这样的变量, 或者直接修改系统, 把组织方式更改一下.
这样更改后, 安装了哪些程序会更直观, 如果包管理器产生一些冲突了(我就有遇到过, 像一些库中的包太旧了, 我自己编译安装它, 但不想要它了, 可是Makefile竟然没有删除的label, 虽然可以手动删除, 但是怕删除错误了), 删除一个程序会更简单些.

静态库和动态库, 如果采用静态编程, 可以把一个执行文件和它的依赖库全编译成一个执行文件(skype, opera类似). 如果使用动态态, 以后更新它的某个库时, 只要它们是API兼容的, 还是可以照样运行. 如果按照我上面的方法更改linux的目录结构的话, 它们的库与头文件与其它程序分享是没有问题的. 但我一直想真正的将所有的程序和它的运行库都独立起来, 除非是系统的东西所有程序就分享, 其它全部私有化, 但不知道怎样会更好, 还是继续考虑或可以的情况下, 做一个快速原形出来.

Post by remote fish

...
要想假如这种直接的冲突,必须要由系统或所有的安装程序达成一个约定,他提供对安装的库的计数。遗憾的是,现在的 linux 没有,也不太可能,提供这种计数,而指望所有的安装程序还维护这样的计数,短期内也是不可能的。
...

这可是一个方法, 或许该加入到包管理器中, 不错的一个想法哦! 我们不求最好, 只求更好. 展开你的想像力去设想怎样才能最好.
别人一直说我们缺少想像力, 研发力不行, 太死板... 这个不是现在谈的重点, 不说它了.
摘一句广告词: "思想有多远, 我们就能走多远!"

我更喜欢将一个应用程序打包成所有linux都可以安装的程序. 我看到有一些打包方法, 如 somefile,bin(它拥有自己的图形化安装方法), somefile.sh(它用脚本在控制, 把安装的二进制文件放在了这个文件中), somefile.package(autopackage打包的安装包).
在linux跨平台安装包中, 我最喜欢autopackage这个包管理器. 它可以与系统本地的包管理器不产生冲突. 如: 有一个用autopackage打包的程序依赖一些库, 它就可以在本系统检测是否有这个库, 如果没有, 可以用本地包管理器安装, 当然也可以使用autopackage安装, 这样autopackage就可以找到它了. 就是说, 它是与本地包协作工作的, 而不是独立的, 这个包管理器有: console安装器, qt界面安装器, gtk界面安装器.  这也是个挺不错的思维!
回复 支持 反对

使用道具 举报

发表于 2007-8-22 22:02:41 | 显示全部楼层
不管库的查找顺序如何,只要它是所有程序都遵守的顺序,那么冲突就有可能产生。同样,只要库放在不是所有程序都遵守的查找路径里,它就有可能无法被别人使用。

不管怎样,除非有特别的理由,还是建议不要使用二进制分发的方式。无论是从授权,从通用性,还是从使用习惯来讲,源代码分发都是最适合 Linux 这样的开发系统的方式。以上是从一个软件提供者的角度来说的。

但是整个话题已经从最初的程序自身的打包方式转移到了包管理方式,那么话题的主语就是系统维护者了。这种情况下,任何想法都可以是合理的,只要遵循一个原则,即不要强制用户接受一种方式,选择权应该在用户手中
回复 支持 反对

使用道具 举报

 楼主| 发表于 2007-8-22 22:20:33 | 显示全部楼层
谢谢大家的讨论, 让我又有了一些新的认识, 这个问题就这样结了.
总结一下:
作为开源软件, 我希望有二进制的tar和源码包二种发布方式(如mldonkey, firefox, thunderbird等等).
作为一款商业软件, 源码发布是不可能的, 只有二进制发布. 如果使用的是自定义的库, 我就使用静态库, 如果是需要链接一些常用的系统库, 就用动态库了(因为有版权的限制, 也是为了让包管理器自动管理).
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表