LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
查看: 3069|回复: 17

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

[复制链接]
发表于 2007-8-17 11:37:10 | 显示全部楼层 |阅读模式
在自己平常使用的软件中, 发现mldonkey, opera(我用的系统Archlinux中的opera是shared版本编译的), skype, MultiGet等静态编译的程序, 觉得它们很不错, 在x86编译的, 方便了在x86环境的移植. 我想把一些小的软件, 把它的依赖库都编译到它的库中, 在另一台没有这样环境的配置中, 也能很好的使用.
于是有如下几个问题:
1. 如何编译静态程序呢? 用gcc -static生成的程序与没用"-static"选项都一样, 文件大小都没变. 写Qt程序, 修改<project>.pro就可以选择编译static版本了, 但也需要static版本安装的Qt, 正在编译中.
2. 如何将一个使用动态库(shared)的程序, 让其使用静态库(static)呢? 用ar打包后可以吗? 正在试验中.
3. MultiGet是怎样将wxgtk的依赖都编译到一个文件中去的呢? 它就一个执行文件MultiGet, 不依赖wxgtk库了.
 楼主| 发表于 2007-8-17 22:29:18 | 显示全部楼层
今天在网上搜了搜信息, 要将Qt编译安装时加上static选项, 才能生成静态库的程序. 呵呵, 这个包可不少哦!
那我想问一下, 一般使用Qt的程序是如此打包的呢? 像skype等Qt商业程序, 没有太多的依赖性, 它们静态编译的不错! 请问它们是中如何打包的呢?
能不能在静态编译的大小和方便性上找个权衡点呢?

很看好PCBSD的单pbi文件, 每个软件包都没有依赖性, windows, macos下好似也是如此吧!
回复 支持 反对

使用道具 举报

发表于 2007-8-18 10:50:15 | 显示全部楼层
库分为动态库(.so)和静态库(.a),以libjpeg 为例,假设它安装在 /usr/lib 下,假如一个程序编译时采用
  1. gcc -o xx xx.c /usr/lib/libjpeg.so
复制代码
刚生成的 xx 会在运行时调用 libjpeg.so 的内容,用 ldd xx 可以发现这一点

  1. gcc -o xx xx.c /usr/lib/libjpeg.a
复制代码

编译时就将 libjpeg 的内容包含在生成的 xx 中,这时 ldd xx 会发现它不需要连接 libjpeg,但 xx 的大小也会变得比较大

  1. gcc -o xx xx.c -ljpeg
复制代码
默认会以先找动态库,找不到再找静态库的方式进行连接,生成的 xx 是上述二者中的一个。

连接静态库与静态编译是有本质区别的,因为基本的 libc 的库是无法以上述的任何方式被包含在 xx 中的,因此仍然不能脱离对 libc 库的依赖,而且连接静态库生成的二进制文件非常大。而静态编译会把其调用的所有的库中其需要的内容包含在生成的二进制文件中,不需要的内容不会被包含进来,因此体积虽然比完全连接动态库的情况要大,但比完全连接静态库一般还是要小的,并且已经可以独立运行。

静态编译的方法很简单

  1. gcc -c a.c -o a.o
  2. gcc -c b.c -o b.o
  3. gcc -static a.o b.o -o xx -ljpeg
复制代码

只要在最终连接的时候回上 -static 参数就 OK 了。在静态编译下,只搜索静态库,因此如果没有提供 libjpeg.a,而只有 libjpeg.so 是不行的
回复 支持 反对

使用道具 举报

 楼主| 发表于 2007-8-18 22:21:20 | 显示全部楼层
谢谢"remote fish"给的解答, 这些我有些明白, 有些不明白. 说白了, 就是对gcc的指令有些不熟悉, 今天花了一天的时间来学习gcc常用指令和makefile常用指令, 它们的参考手册也大概看了看, 觉得还不错. 建议linux的程序员都先学习一下这些工具的用法, 不然你会走很多弯路的.

明天再下载shype和其它Qt的商业程序, 看看它们的依赖些什么. Archlinux上的opera是share编译的, 仍然看到它依赖了/opt/qt/lib/libqt-mt.so.3的文件.
skype吧, 在我的debian(debian是gnome环境, 没有Qt库)下试过, 可以正常运行, 那时没有注意到Qt程序打包的问题, 估计skype是静态编译的. 觉得也不错啊, 大小就在20M左右. 一般一个win的程序, 也就是这样了.
也可以这么估计, win的程序只依赖了最基本的.net库或是mfc的一个库文件, 就好像"remote fish"说的一样, 最基本的libc的库是无法被包含的, 那win的程序也就大多都是静态编译的喽.

但是这样有些麻烦, 如: 每个库都要编译成静态库和动态库, 我看过/usr/lib下的库文件, 很多库都有动态库和静态库二种版本. win下的程序, 很多库也是分别有dll和lib两种版本.
呵呵, 我想到能不能将Share Object库转成Archive库, 用ar打包成Archive库. 结果根本就不成, 这也肯定是的, Archive库需要用somefile.o库用ar命令打包成静态库.
不知道能否分析linux下程序的依赖库, 将它们和程序一起打包发布, 安装时再将它们安装回去, 就类似win下的程序打包发布的工具InstallShield了. linux还没有见到过这样的工具. 使用过几款linux商业软件, 非java写的程序都是静态编译, 用linux下常见的somefile.tar.gz,somefile.tar.bz2打包, 解压到一个地方后直接使用. 如果是java写的程序, 有些就有一个图形化的安装工具了, 也属于与这个程序配置的, 并不是一个通用的程序打包发布工具.

因为工作时, 大多都在公司做出来的是商业程序, 商业程序肯定得保持良好的移植性和便利的安装性, 那一般就是静态编译了, 如果是嵌入式程序呢? 那些静态库不是挺占用空间吗? 像一个Qt3程序, 你就得背上几M的一个静态库(Qt4把库文件分的挺细了, 不错). 不知道有没有更好的方法发布程序.
回复 支持 反对

使用道具 举报

 楼主| 发表于 2007-8-18 22:37:01 | 显示全部楼层
我正在试验能否在发布时带上动态链接库, 就是把程序的依赖动态链接库放到本程序下, 然后再编译, 运行时, 依赖库文件就在本程序下, 增加了移植性. 初步试验Qt程序, 不成功! 再看看,再试试 有经验的朋友请指教一下哦! 谢谢了.
回复 支持 反对

使用道具 举报

发表于 2007-8-18 23:39:45 | 显示全部楼层
“不能把 libc 包含”这是指非静态编译情况而言(静态连接或动态连接),对于静态编译来说,是会把库的内容包含进来的,但并不是完整的库,而只是需要的内容

除非特别的目的,个人认为最好还是不要把二进制程序和其依赖库一起打包比较好,这种方式其实是某些非开源操作系统或应用软件的无奈之举,它们尽管可以在某种意义上简化安装,但是会使打包后的安装程序变大,而且多个程序可能会提供相同的甚至冲突的库,这很麻烦的;另一方面,通用的安装包的可行性也是由于某非开源操作系统的版本系列相对贫乏,软件供应者可以针对一个有限的预期支持对象集合来实现其安装包,但对于其预期支持对象之外的系统版本,软件甚至会完全无法安装或使用。

对于 Linux 这样的开源系统来说,最合适的软件安装方式是源码编译,当然,这较适合专业用户,如程序员,这类用户也是 Linux 这样的操作系统最初面向的用户群;对于普通的用户来说,软件供应者即使要提供预编译包最好也不是把依赖库打包,而是提供其依赖关系,再由系统去处理依赖关系,这种做法的好处是可以最大程度的避免冲突。个人认为 Debian 是这方面做得很好的,只要理解了 Debian 的包管理方式,Debian 可以提供最为简单有效的软件维护方法。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2007-8-20 08:07:00 | 显示全部楼层
谢谢, 再次感谢"remote fish"的耐心解答.

Post by remote fish

而且多个程序可能会提供相同的甚至冲突的库,这很麻烦的;
Post by remote fish

另一方面,通用的安装包的可行性也是由于某非开源操作系统的版本系列相对贫乏,软件供应者可以针对一个有限的预期支持对象集合来实现其安装包,但对于其预期支持对象之外的系统版本,软件甚至会完全无法安装或使用。

呵呵, 我来针对你说的这两点, 谈谈我的想法吧, 本人才疏学浅, 说错的地方还请指来.
1. 相同的静态库, 多个版本会有冲突吗? 这应该很少或不会吧, 我觉得linux解决版本冲突问题有些方面较好, 也有些不好, 如: 相同的库可以安装上多个版本的库, libfoo-1.xx.a libfoo-2.xx.a,  libfoo.so.1.x.x libfoo.so.2.x.x等等. 在链接时指定某版本的库, 在程序运行时, 它只会去查找这个文件是否存在, 如果不存在, 就会报错了吧!
商业程序, 如果好点的公司, 遵守OpenLicense, LGPL, BSD的话就把它们链接进来, 做成一个他们自己的静态库, 这样就不与会原来的库名冲突了吧, 我猜想它们也会这样做.
2. 你说商业程序的版本升级不会很频繁是吧, 也的确, 商业程序追求的是稳定性. 但如果我们系统的库更新较快, 而商业程序又使用了较老的库, 它们就冲突了, 我们就又得为它装上低版本的库了. 常规发布, 如果不是使用的linux自带的包管理器安装, 依赖的库升级了, 这时程序又会出问题了.
如我的几个真实例子: 1. 我的系统更新很频繁, 在debian下时, 我使用的是dist源, 也基本上是每天更新. 如我用过的navigator, 它就依赖比我低一版本的libstdc++库, 你就不得不为它又重新装上一次了. 2. 在更新Archlinux的朋友们, 在今年初的时候肯定遇到过Opera不能启动的问题, 就是因为它依赖的一个库(xorg的哪个库)版本更新了, 导致它不能正常启动了.在么你换上Opera依赖的旧库, 要么等官方解决它. 还好, Opera动作比较迅速, 很快推出了新版本. 但你很难保证所有的商业公司都这样.

Post by remote fish

对于 Linux 这样的开源系统来说,最合适的软件安装方式是源码编译,当然,这较适合专业用户,如程序员,这类用户也是 Linux 这样的操作系统最初面向的用户群;对于普通的用户来说,软件供应者即使要提供预编译包最好也不是把依赖库打包,而是提供其依赖关系,再由系统去处理依赖关系,这种做法的好处是可以最大程度的避免冲突。个人认为 Debian 是这方面做得很好的,只要理解了 Debian 的包管理方式,Debian 可以提供最为简单有效的软件维护方法。

对普通用户, 商业程序一般提供的是针对不同平台提供预编译版本, 如果是基于某些发行版, 可以交由系统去维护它. 但是一个不太有名气的linux发行版, 使用不同的包管理器, 那他也很难提供全吧, 这不得不靠用户把其它平台下的版本转来了, 这时依赖包又改变了(如debian对包分的较细, 而archlinux没有debian分的那样细).

我看了skype的发布方法:
Skype 1.4 Beta 版提供有下列 Linux 发行版的安装包:
Ubuntu Feisty Fawn (7.04)
Fedora Core 6/7
Debian Etch
OpenSUSE 10+
Mepis
Mandriva
Xandros
如果你所用的 Linux 发行版不属于以上任何一种,那么也可以使用 Skype 的通用安装包。


skype也是有依赖其它库的. 但它有两种版本, 一种是没有包含把Qt静态库包含和其中, 一种是把静态库包含在其中了. 至于其它库嘛,我看过它的协议了, Qt采用的是双协议, 如果购买了商业许可, 当然可以静态包含在项目中了; libsigc++ 和libasound(alsa)都是使用的LGPL协议, 也可以动态和静态包含, 估计是为了减少skype的大小.
Software requirements
Qt 4.2.1+
D-Bus 1.0.0
libsigc++ 2.0.2
libasound2 1.0.12


看来在linux下, 开源软件都采用了几种发式发行, 1. 使用各大常用的发行版, 让包管理器来解决依赖问题 2.常规的tar包发布.
我也思考一下, 能否很好的解决linux软件包发行的问题呢? 也希望大家共同讨论一下!

我吧, 喜欢开源, 也不排斥商业环境. 开源, 保证了资源的共享, 促进了软件行业的交流和进步. 商业软件保持了软件行业的创新, 为程序员提供了生存的环境.
回复 支持 反对

使用道具 举报

 楼主| 发表于 2007-8-20 08:09:59 | 显示全部楼层
我在想, 在写程序时, 就是否考虑不要依赖过多的库呢? 但这样, 又造成了重复劳动. ...
回复 支持 反对

使用道具 举报

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

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

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

使用道具 举报

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

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

使用道具 举报

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

本版积分规则

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