我在国内外Qt论坛上看到的最多的问题之一就是如何编译静态Qt库,可见在动态链接库 (Windows: .dll, Linux: .so)大势所趋的今天,静态链接库还是具有其存在的合理性和广泛需求。首先看看动态库给我们带来了什么:
- 开发时的复用性:成千上万的各种实用库,包含它们的头文件,你就可以开始使用它们提供的功能;
- 编译时的快捷化:进行过静态链接编译的人都知道,其编译过程的时间消耗是动态链接的数倍倍甚至更多;
- 部署时的轻量化:如果你的程序使用操作系统自带的动态库,或者是一些非常流行的库。那么部署你的软件时,这些库便可以不用包含在你的packge里面,大大减小了其体积;
- 使用时的可维护性:某个库如果发布了更新的版本,那么你只需替换这个动态库文件,而不需要重新编译你的整个程序。
在如今这个时不时都需要给软件打补丁的时代,这是相当方便的。那么,她又会给我们带来什么烦恼呢?软件的易用性至上。所谓易用性,不单单指这个进入了这个软件后使用起来多么多么方便,使用之前的那些过程同样也是易用性的一部分。
用过Linux的可能都会有同感,装个软件真难啊,一会儿提示缺这个so库,一会儿这个so和那个so又不兼容了,还有甚者,你还需要在你的帐户的配置文件里设置一大堆参数……还没安装完,就已经放弃了使用这个软件的念头。我想,易用性的问题也是 Linux如此强大可是普及率远远不及Windows的根源吧,毕竟绝大多数的用户需要的是一个傻瓜相机般的操作系统。
不过在Windows下,常常你也会遇到”某个dll无法找到,程序无法启动”、”需要.net framework”之类的提示之。懂点软件知识的,还知道去下载这个缺失的dll库或者是下载.net的再发行库来安装。但是这已经大大违背了软件易用性的原则,要知道软件的受众绝大多数都是与IT行业无关的人员,它们遇到这些问题时候便是束手就擒。
Visual Studio 2005后的版本更是不可理喻,它们编译出来的C/C++程序,如果在没有Visual Studio再发行库的系统上运行,直接提示”应用程序没有正确配置,重新安装可能解决该问题”,简直不知所云……
静态链接的存在的合理性就在这里,一个可执行文件部署给用户,用户什么都不用做,双击一下就可享用。至于编译、维护,那是开发者的事情,哪怕一次编译要耗费一天的时间,也不能浪费用户的时间去做一些对他来说不知所云而本可以在开发者的环节中完成的事情。基于以上的信念,我研究了Windows下真正意义上的Qt静态库编译方法。至于什么叫”真正意义上”,看了下文便知。
Visual Studio相关编译选项
自己动手编译过Qt的人可能会觉得奇怪,拿这个问题来写这么一大段,真是小题大作。Qt的配置选项中不是写得清楚明白-static便是编译静态库吗。可是你有真正试过吗,用这个静态库编译的程序拿到一个”干净”的机器上运行,则提示”应用程序没有正确配置……”或是”msvcrpxx.dll”没有找到。 看来Qt是静态了,可是Qt是用C++写的,其中链接的C++运行库还是动态的。解决这个问题,要从Visual Studio的4个编译选项说起,它们决定了程序在链接阶段C/C++库的链接方式。
1.链接C/C++多线程动态库,使用这些编译选项,软件部署时需要VC的再发行库,否则就会出现上述错误提示。
/MD:动态链接多线程库(msvcrt.lib)。使用该选项时,需要用/NODEFAULTLIB选项来忽略掉libc.lib、libcmt.lib、libcd.lib、libcmtd.lib、msvcrtd.lib库,否则会有链接错误;
/MDd:动态链接多线程调试库(msvcrtd.lib)。使用该选项时,需要用/NODEFAULTLIB选项来忽略掉libc.lib、libcmt.lib、msvcrt.lib、libcd.lib、libcmtd.lib库,否则会有链接错误;
2.链接C/C++多线程静态库,使用这些编译选项,软件部署时不需要VC的再发行库。
/MT:静态链接多线程库(libcmt.lib)。使用该选项时,需要用/NODEFAULTLIB选项来忽略掉libc.lib、msvcrt.lib、libcd.lib、libcmtd.lib、msvcrtd.lib库,否则会有链接错误;
/MTd:静态链接多线程调试库(libcmtd.lib)。使用该选项时,需要用/NODEFAULTLIB选项来忽略掉libc.lib、libcmt.lib、msvcrt.lib、libcd.lib、msvcrtd.lib库,否则会有链接错误。
OK,准备工作就绪,下面开始编译真正意义上的静态Qt库:-)
编译静态Qt库
既然找到了解决方法,这个编译就应该没问题了吧。可是configure时明明用-static配置了Qt,编译结果确仍然事与愿违。仔细查看所生成的 makefile,才发现编译选项都是/MD、/MDd。问题就出在这里啦,不过这成千上百个makefile,难道要手动一个个修改……一定有什么东西决定了makefile的生成参数。不错!就在Qt根目录中的mkspecs目录里,一看名字就知道是make specification的缩写。该目录下,各种平台下的各个编译器都有一个子目录。嘿嘿,编译器的编译选项就都在这里了,打开win32- msvc2008中的qmake.conf看看,找到了下面两行吗:
[cc lang="make"]
QMAKE_CFLAGS_RELEASE = -O2 -MD -GL
QMAKE_CFLAGS_DEBUG = -Zi -MDd
[/cc]
相信你已经知道了问题答案,将它们改成:
[cc lang="make"]
QMAKE_CFLAGS_RELEASE = -O2 -MT -GL
QMAKE_CFLAGS_DEBUG = -Zi -MTd
[/cc]
同时别忘了加入前面提到的忽略库选项,修改QMAKE_LFLAGS_RELEASE和QMAKE_LFLAGS_DEBUG参数为:
[cc lang="make"]
QMAKE_LFLAGS_RELEASE = /INCREMENTAL:NO /LTCG /NODEFAULTLIB:libc.lib /NODEFAULTLIB:msvcrt.lib/NODEFAULTLIB:libcd.lib /NODEFAULTLIB:libcmtd.lib /NODEFAULTLIB:msvcrtd.lib
QMAKE_LFLAGS_DEBUG = /DEBUG /NODEFAULTLIB:libc.lib /NODEFAULTLIB:libcmt.lib /NODEFAULTLIB:msvcrt.lib /NODEFAULTLIB:libcd.lib /NODEFAULTLIB:msvcrtd.lib
[/cc]
大功告成,这个时候再在Qt的根目录下configure -static …(其它选项自行选择),然后nmake,两三个小时之后,真正意义上的静态Qt库就产生了。本人有个小建议,其实编译demos和examples 会消耗非常多时间,你只用cd src然后nmake来编译Qt库,如有需要cd tools来编译诸如QtDesigner之类的工具。另外webkit库的编译是最耗时的,如果你不准备使用这个库,那么configure加入 -no-webkit,这样,整个编译过程大概值耗时40-60分钟。这时候你可以尝试建立第一个纯静态的Qt程序,不过当你链接的时候,仍然会出现一大堆链接错误,提示 unresolved external symbol。这个时候你只需要在你的Qt工程文件.pro里面加入:
[cc lang="make"]
win32:LIBS += Imm32.lib Winmm.lib ws2_32.lib
[/cc]
即可。至此,你的”纯”静态Qt应用程序就可以发布给用户啦。
著作权信息(站外使用本文请保留以下内容)
| 文章作者:Tim 原始站点:嘻来嚷往 – IF YOU SEE SOMETHING, SAY SOMETHING. 原文标题:编译Windows平台下真正意义上的静态Qt库 发表日期:2009年06月26日 原文链接:http://xirang.ca/2009/06/qt-static |
|
| 版权协议:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可,作品中的文字和图像若非明确指出著作权归属则均受到该协议法律文本的保护。站外使用请严格遵守协议,嘻来嚷往站保留诉诸法律的权利!如有异议请与站长联系。 | ![]() |


静态库是好,只是编译出来的程序要大很多
哇噻,这篇文章终于有人评论了,泪流满面……
不能这么说,动态编译的程序,还不是要带上动态库才能运行,本质都是一堆完成某项功能的二进制代码,总体积跟单个静态的基本一致,只不过静态的把他们放一堆了。
那如果同一台机器上同时跑着几个静态编译的程序 他们中大部分是重复的 浪费了系统资源 当然 现在vs2005运行库还不普及 以及现在系统资源不成问题 静态程序还是很方便的
每个程序在内存中都有独立的代码段、数据段和堆栈段的,相互之间不能越界访问吧?就算不同程序加载了同一个动态链接库,也是分别将动态链接库的代码加载到了这两个程序的独立的内存空间中,这与静态库没区别。
你可以自己试试编译一个同样程序静态和动态的副本,然后运行多个实例,查看下他们的内存使用量就知道了。
哦 那我算明白了 浪费的仅仅是一些硬盘空间?
可以这么说。但是,静态编译的程序没有加载动态库的时间损耗,照理说比动态的快点,不过在当今CPU的速度下,这点时间也是感觉不出来的。
求助一下 把以前的程序静态编译了一切正常 唯独JPEG不工作了
检查发现jpeg的解码器在我的库里是以插件形式存在的… 我是要把那个插件的.h.lib拷过来重编译么? 还是把dll拿过来就行了?
恩 先做实验去
我觉得在你的大作下讨论细节问题不太好 把我的留言删了吧 不过你要是可以在文章里补充一下 静态编译qt时插件的编译方法和注意事项那就perfect了 :)
没关系的,尽管讨论。建议很好,我会把静态程序使用Qt插件的问题补充进去,谢谢coxoto :)
这是一个4.5.1之后Qt版本出现的问题,要让插件能在静态编译的程序中工作,需要在程序的main.cpp中用Q_IMPORT_PLUGIN()宏导入插件:
#ifdef _STATIC_RELEASE
#include <QtPlugin>
Q_IMPORT_PLUGIN(qico);
Q_IMPORT_PLUGIN(qjpeg);
Q_IMPORT_PLUGIN(qgif);
#endif
这个_STATIC_RELEASE宏是我自己定义的,我调试的时候还是用动态编译,最终发布的时候才用静态编译,静态编一次时间太长了……
感谢Tim 终于work了
偷偷补充一下 于此同时 要在pro文件中加入
QTPLUGIN += qico
QTPLUGIN += qjpeg
QTPLUGIN += qgif