Qt编写的应用发布到用户手里,同时也要把Qt库一起打包给用户。方法有很多。
Qt编写的应用发布到用户手里,同时也要把Qt库一起打包给用户。方法有很多。
1. 简单又麻烦的办法,静态编译Qt
简单是因为静态编译的Qt库会随着应用一起发布,麻烦是我们必须自己编译静态的Qt。而且,很多情况下我们不仅是要使用Qt,还有很多其它的库,如果每一个都静态编译,我们的执行文件就太巨大了。所以简单的应用,静态编译可能是好的,但是,比较大的项目,这种方法就不推荐了。
简单是因为静态编译的Qt库会随着应用一起发布,麻烦是我们必须自己编译静态的Qt。而且,很多情况下我们不仅是要使用Qt,还有很多其它的库,如果每一个都静态编译,我们的执行文件就太巨大了。所以简单的应用,静态编译可能是好的,但是,比较大的项目,这种方法就不推荐了。
2. 好又不麻烦的办法,使用Frameworks
Mac中的GUI应用必须以Bundle的形式运行,所谓Bundle,是一个以".app"结尾命名的文件夹,系统自动识别它为一个应用包,应用所有的东西(执行文件、链接的动态库、资源文件等等)都在里面了,打开应用直接"open myApp.app"就可以了,安装的时候直接把Bundle拖到Finder里就行了。卸载的时候直接把Bundle删除就行了。多让人省心。
Bundle的结构如下图:(从Qt文档里借来的)
我们如果要随着我们的应用一起发布Qt库,比较合理的就是把所有需要的Qt库都复制到Frameworks目录中。
使用otool可以查看一个应用都链接了哪些动态库
$ otool -L myApp.app/Contents/MacOS/myApp
myApp.app/Contents/MacOS/myApp:
libqxmpp.0.dylib (compatibility version 0.7.0, current version 0.7.4)
/usr/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.2.5)
QtWebKit.framework/Versions/4/QtWebKit (compatibility version 4.9.0, current version 4.9.3)
QtXml.framework/Versions/4/QtXml (compatibility version 4.8.0, current version 4.8.3)
QtCore.framework/Versions/4/QtCore (compatibility version 4.8.0, current version 4.8.3)
QtGui.framework/Versions/4/QtGui (compatibility version 4.8.0, current version 4.8.3)
QtNetwork.framework/Versions/4/QtNetwork (compatibility version 4.8.0, current version 4.8.3)
/usr/lib/libstdc++.6.dylib (compatibility version 7.0.0, current version 56.0.0)
/usr/lib/libgcc_s.1.dylib (compatibility version 1.0.0, current version 1669.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 169.3.0)
我们用了QtWebKit、QtXml、QtCore、QtGui、QtNetwork,然后挨个Copy吧。
$ cp -R /path/to/Qt/lib/QtCore.framework myApp.app/Contents/Frameworks
......
......
真的悲惨到到自己手工去复制?当然不是,我们都是懒人啊,Qt自身带了一个macdepolyqt的神器,一切就变得简单了。
$ macdeployqt myApp.app
ERROR: no file at "/usr/lib/libqxmpp.0.dylib"
qxmpp是我们用的一个三方库,并不在/usr/lib下,找不到并不奇怪,怎么才能让macdeployqt找到这个三方库呢?
Mac下是用DYLD_LIBRARY_PATH来寻找动态库的,类似于Linux下的LD_LIBRARY_PATH。otool -L输出的信息,其实是保存在编译好的可执行文件里的。libqxmpp.0.dylib没有指定绝对路径,系统会到DYLD_LIBRARY_PATH中去寻找它,macdepolyqt并不理会DYLD_LIBRARY_PATH,所以,我们只好将libqxmpp.0.dylib这一条修改成它的绝对路径,让macdeployqt可以找到它。
install_name_tool -change "libqxmpp.0.dylib" "/opt/library/qxmpp/lib/libqxmpp.0.dylib" myApp.app/Contents/MacOS/myApp
如此以后就没问题了
$ macdeployqt fytclient.app
$ otool -L myApp.app/Contents/MacOS/myApp
myApp.app/Contents/MacOS/myApp:
@executable_path/../Frameworks/libqxmpp.0.dylib (compatibility version 0.7.0, current version 0.7.4)
/usr/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.2.5)
@executable_path/../Frameworks/QtWebKit.framework/Versions/4/QtWebKit (compatibility version 4.9.0, current version 4.9.3)
@executable_path/../Frameworks/QtXml.framework/Versions/4/QtXml (compatibility version 4.8.0, current version 4.8.3)
@executable_path/../Frameworks/QtCore.framework/Versions/4/QtCore (compatibility version 4.8.0, current version 4.8.3)
@executable_path/../Frameworks/QtGui.framework/Versions/4/QtGui (compatibility version 4.8.0, current version 4.8.3)
@executable_path/../Frameworks/QtNetwork.framework/Versions/4/QtNetwork (compatibility version 4.8.0, current version 4.8.3)
/usr/lib/libstdc++.6.dylib (compatibility version 7.0.0, current version 56.0.0)
/usr/lib/libgcc_s.1.dylib (compatibility version 1.0.0, current version 1669.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 169.3.0)
这时候再看otool的结果,qxmpp以及Qt的动态库都指向了Bundle里Frameworks目录下了。@executable_path顾名思义就是应用的执行目录,也就是myApp.app/Contents/MacOS
发布ios QT程序
用 lipo 下令裁剪出需要的 architecture
iOS 上的 framework 和 .a 库一般会同时支持多个 architecture, 比如 i386, armv7, armv7s 等。
为了减小程序的尺寸,可能需要裁剪掉不需要的 architecture, 这时就需要使用 lipo 命令给程序瘦身。
Step1: 查看目标库文件支持的 architecture, 两个选择
1.1 lipo -info 目标库文件
1.2 file 目标库文件
Step2: 生成只支持一个 architecture 的 库文件
lipo 目标库文件 -thin 目标architecture 目标文件名
eg: lipo MVC -thin i386 MVC.i386
eg1: lipo MVC -thin armv7 MVC.armv7
最后,删掉旧的库文件,把目标库文件改成原来的名字,进行替换即可。
另一个发布QT程序到mac app store的文章:
Mac中的GUI应用必须以Bundle的形式运行,所谓Bundle,是一个以".app"结尾命名的文件夹,系统自动识别它为一个应用包,应用所有的东西(执行文件、链接的动态库、资源文件等等)都在里面了,打开应用直接"open myApp.app"就可以了,安装的时候直接把Bundle拖到Finder里就行了。卸载的时候直接把Bundle删除就行了。多让人省心。
Bundle的结构如下图:(从Qt文档里借来的)
我们如果要随着我们的应用一起发布Qt库,比较合理的就是把所有需要的Qt库都复制到Frameworks目录中。
使用otool可以查看一个应用都链接了哪些动态库
$ otool -L myApp.app/Contents/MacOS/myApp myApp.app/Contents/MacOS/myApp: libqxmpp.0.dylib (compatibility version 0.7.0, current version 0.7.4) /usr/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.2.5) QtWebKit.framework/Versions/4/QtWebKit (compatibility version 4.9.0, current version 4.9.3) QtXml.framework/Versions/4/QtXml (compatibility version 4.8.0, current version 4.8.3) QtCore.framework/Versions/4/QtCore (compatibility version 4.8.0, current version 4.8.3) QtGui.framework/Versions/4/QtGui (compatibility version 4.8.0, current version 4.8.3) QtNetwork.framework/Versions/4/QtNetwork (compatibility version 4.8.0, current version 4.8.3) /usr/lib/libstdc++.6.dylib (compatibility version 7.0.0, current version 56.0.0) /usr/lib/libgcc_s.1.dylib (compatibility version 1.0.0, current version 1669.0.0) /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 169.3.0)
我们用了QtWebKit、QtXml、QtCore、QtGui、QtNetwork,然后挨个Copy吧。
$ cp -R /path/to/Qt/lib/QtCore.framework myApp.app/Contents/Frameworks
......
......
真的悲惨到到自己手工去复制?当然不是,我们都是懒人啊,Qt自身带了一个macdepolyqt的神器,一切就变得简单了。
$ macdeployqt myApp.app ERROR: no file at "/usr/lib/libqxmpp.0.dylib"
qxmpp是我们用的一个三方库,并不在/usr/lib下,找不到并不奇怪,怎么才能让macdeployqt找到这个三方库呢?
Mac下是用DYLD_LIBRARY_PATH来寻找动态库的,类似于Linux下的LD_LIBRARY_PATH。otool -L输出的信息,其实是保存在编译好的可执行文件里的。libqxmpp.0.dylib没有指定绝对路径,系统会到DYLD_LIBRARY_PATH中去寻找它,macdepolyqt并不理会DYLD_LIBRARY_PATH,所以,我们只好将libqxmpp.0.dylib这一条修改成它的绝对路径,让macdeployqt可以找到它。
install_name_tool -change "libqxmpp.0.dylib" "/opt/library/qxmpp/lib/libqxmpp.0.dylib" myApp.app/Contents/MacOS/myApp
如此以后就没问题了
$ macdeployqt fytclient.app $ otool -L myApp.app/Contents/MacOS/myApp myApp.app/Contents/MacOS/myApp: @executable_path/../Frameworks/libqxmpp.0.dylib (compatibility version 0.7.0, current version 0.7.4) /usr/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.2.5) @executable_path/../Frameworks/QtWebKit.framework/Versions/4/QtWebKit (compatibility version 4.9.0, current version 4.9.3) @executable_path/../Frameworks/QtXml.framework/Versions/4/QtXml (compatibility version 4.8.0, current version 4.8.3) @executable_path/../Frameworks/QtCore.framework/Versions/4/QtCore (compatibility version 4.8.0, current version 4.8.3) @executable_path/../Frameworks/QtGui.framework/Versions/4/QtGui (compatibility version 4.8.0, current version 4.8.3) @executable_path/../Frameworks/QtNetwork.framework/Versions/4/QtNetwork (compatibility version 4.8.0, current version 4.8.3) /usr/lib/libstdc++.6.dylib (compatibility version 7.0.0, current version 56.0.0) /usr/lib/libgcc_s.1.dylib (compatibility version 1.0.0, current version 1669.0.0) /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 169.3.0)
这时候再看otool的结果,qxmpp以及Qt的动态库都指向了Bundle里Frameworks目录下了。@executable_path顾名思义就是应用的执行目录,也就是myApp.app/Contents/MacOS
发布ios QT程序
iOS 上的 framework 和 .a 库一般会同时支持多个 architecture, 比如 i386, armv7, armv7s 等。
为了减小程序的尺寸,可能需要裁剪掉不需要的 architecture, 这时就需要使用 lipo 命令给程序瘦身。
Step1: 查看目标库文件支持的 architecture, 两个选择
1.1 lipo -info 目标库文件
1.2 file 目标库文件
Step2: 生成只支持一个 architecture 的 库文件
lipo 目标库文件 -thin 目标architecture 目标文件名
eg: lipo MVC -thin i386 MVC.i386
eg1: lipo MVC -thin armv7 MVC.armv7
最后,删掉旧的库文件,把目标库文件改成原来的名字,进行替换即可。
另一个发布QT程序到mac app store的文章:
基于Qt开发的应用程序进驻Mac App Store
最近摸索着将一个基于用Qt开发
的App发布到Mac App Store
,在网上查找相关资料的时候,发现很多文档都过期了,要有一些修改,所以想记录下来,希望能帮助有需要的同学,让他们少走一些弯路。
这里我们假设你已经有一个Mac开发者账号
,成功安装好了相关证书,并且已经准备好你的App了(release版,但未部署)。
最近摸索着将一个基于用Qt开发
的App发布到Mac App Store
,在网上查找相关资料的时候,发现很多文档都过期了,要有一些修改,所以想记录下来,希望能帮助有需要的同学,让他们少走一些弯路。
这里我们假设你已经有一个Mac开发者账号
,成功安装好了相关证书,并且已经准备好你的App了(release版,但未部署)。
第一步:打包
在Mac下,对基于Qt应用程序的部署,官方文档这里已经讲解很详细了
我用的是framework的方式
,大致意思就是要把所依赖的库和插件拷贝到App的Bundle里,然后修改程序的依赖链接,让其启动时加载的是Bundle里的库和插件。
Qt自带的macdepolyqt
工具能够很方便的帮助你实现这一目的。
虽然很多人觉得这个工具并不完美,会把一些实际上用不到的插件和库也会拷贝进去
这是因为它本身的判断方式,比如你的程序用到了QtSql.framework, 那么它就会把所有的sqldrivers目录中的插件都拷贝过来,而不管你的程序有没有用到,这让你的程序变得臃肿)
但是对于初学者,我相信你更喜欢用这种方式,因为它相当简单
。
假如你是一个完美主义者,对自己的App有着严格的要求,那么建议你自己写Shell Script
去把程序用到的Framework和Plugins都拷贝进去,然后修改依赖链接。
Xcode自带指令帮你查看程序依赖哪些库,库又依赖了哪些库,插件依赖了哪些库,如果想要知道程序是如何加载插件的,可以查阅这里,当然也有指令修改这些依赖。这些都可以在上面提供的链接里找到。
在Mac下,对基于Qt应用程序的部署,官方文档这里已经讲解很详细了
我用的是
framework的方式
,大致意思就是要把所依赖的库和插件拷贝到App的Bundle里,然后修改程序的依赖链接,让其启动时加载的是Bundle里的库和插件。
Qt自带的macdepolyqt
工具能够很方便的帮助你实现这一目的。
虽然很多人觉得这个工具并不完美,会把一些实际上用不到的插件和库也会拷贝进去
这是因为它本身的判断方式,比如你的程序用到了QtSql.framework, 那么它就会把所有的sqldrivers目录中的插件都拷贝过来,而不管你的程序有没有用到,这让你的程序变得臃肿)
但是对于初学者,我相信你更喜欢用这种方式,因为它相当简单
。
假如你是一个完美主义者,对自己的App有着严格的要求,那么建议你自己写Shell Script
去把程序用到的Framework和Plugins都拷贝进去,然后修改依赖链接。
Xcode自带指令帮你查看程序依赖哪些库,库又依赖了哪些库,插件依赖了哪些库,如果想要知道程序是如何加载插件的,可以查阅这里,当然也有指令修改这些依赖。这些都可以在上面提供的链接里找到。
第二部:sandbox 和 info.plsit文件
Windows程序员可能不知道什么是sandbox
。赶紧恶补一下,可以去google一下或者看下Apple的官方文档
你要有一个.entitlements
的文件(为第三步做准备),里面就是你需要的sandbox权限(其实就是个xml格式的文件,记录着一些键值对,你可以用Xcode新建一个项目,然后勾选需要的sandbox权限,生成的.entitlements
文件就可以拿过来用了)。
如果没有勾选相关的权限,在签名之后是无法正常使用相关功能的。
这里说的info.plist
文件是整个程序的,在App所在目录/Contents
下和后面说的info.plist文件不是同一个(但作用都是一样的)。
Qt默认生成的info.plist 文件是不符合上Mac App Store要求的,需要额外加一些键值对,比如能运行的最低系统版本信息等等。详情可以参看Apple的文档。
Windows程序员可能不知道什么是sandbox
。赶紧恶补一下,可以去google一下或者看下Apple的官方文档
你要有一个.entitlements
的文件(为第三步做准备),里面就是你需要的sandbox权限(其实就是个xml格式的文件,记录着一些键值对,你可以用Xcode新建一个项目,然后勾选需要的sandbox权限,生成的.entitlements
文件就可以拿过来用了)。
如果没有勾选相关的权限,在签名之后是无法正常使用相关功能的。
这里说的info.plist
文件是整个程序的,在App所在目录/Contents
下和后面说的info.plist文件不是同一个(但作用都是一样的)。
Qt默认生成的info.plist 文件是不符合上Mac App Store要求的,需要额外加一些键值对,比如能运行的最低系统版本信息等等。详情可以参看Apple的文档。
第三部:签证书
在完成上一步后,所有用到的库和插件都已经拷贝到程序所在的Bundle里面了
。
可以按照这里的说明进行Code Signing
,但是这里需要注意的是,
在签证书之前必须手动的把每个Framework下面的info.plist文件考到相应的目录下
以QtCore.framework
为例,你需要将Qt安装目录/5.2.0/clang_64/lib/QtCore.framework/Contents/info.plist
拷贝到App所在目录/Contents/Frameworks/QtCore.framework/Resources/
, 否则在给Framework签证书的时候会失败,当然会导致最后给整个Bundle签证书的时候失败。签证书的顺序应该是先签里面的Framework 和Plugins,最后签整个Bundle。
签成功之后,除了Plugins,每个Framework的子目录内和Contents目录下都会有一个_CodeSignature
的文件夹。
Ps:在成功之后不要忘了保存你所用到的命令行,就可以做一个脚本了,下次在打包部署的时候就方便多了。这里是我用到的脚本,大家可以下载下来参看。欢迎大家给我提意见和建议。
在完成上一步后,所有用到的库和插件都已经拷贝到程序所在的Bundle里面了
。
可以按照这里的说明进行Code Signing
,但是这里需要注意的是,
在签证书之前必须手动的把每个Framework下面的info.plist文件考到相应的目录下
以QtCore.framework
为例,你需要将Qt安装目录/5.2.0/clang_64/lib/QtCore.framework/Contents/info.plist
拷贝到App所在目录/Contents/Frameworks/QtCore.framework/Resources/
, 否则在给Framework签证书的时候会失败,当然会导致最后给整个Bundle签证书的时候失败。签证书的顺序应该是先签里面的Framework 和Plugins,最后签整个Bundle。
签成功之后,除了Plugins,每个Framework的子目录内和Contents目录下都会有一个_CodeSignature
的文件夹。
Ps:在成功之后不要忘了保存你所用到的命令行,就可以做一个脚本了,下次在打包部署的时候就方便多了。这里是我用到的脚本,大家可以下载下来参看。欢迎大家给我提意见和建议。
如何给Qt 开发的应用程序加图标
用写字版新建个文件
里面就写一行:
IDI_ICON1 ICON DISCARDABLE "myappico.ico"
保存改名为 myapp.rc并把它和你的图标myappico.ico一起复制到你的QT工程项目的目录。
用写字版打开你的QT工程文件(如 "myapp.pro" ),在里面最后新添一行
RC_FILE = myapp.rc