Qt5.5文档翻译:用于OS X系统的Qt——部署,Qt for OS X - Deployment
这篇文档,说明的是,如何创建一个 OS X 程序包,并且确保 该程序在运行时能够找到它所需要的资源。 为了演示这个过程,我们会在此文档中说明如何Qt 部署安装包自带的那个 Plug & Paint 示例程序。
OS X 版的Qt 安装程序中包含了一个 部署工具 ,它会自动完成这里所描述的过程。
在OS X系统中,一个图形界面的程序,必须被作为一个应用程序包来构建及运行,这种应用程序包,是一个目录结构,在Finder 中会呈现为一个单个的实体。一个应用程序包,一般会包含着可执行程序以及它所需要的所有资源。下面是某个应用程序包结构的截屏:
应用程序包,为用户提供了狠多优点:
•.它狠容易安装,因为,它被看作是一个单个的实体。
•.可从代码中获取应用程序包中的信息。
这种特性,是 OS X独有 的,已经超出了本文的研究范围。欲知更多关于应用程序 包的信息,则访问 苹果公司 的开发者网站 。
qmake 会自动地为妳的应用程序生成一个应用程序包。要禁用这种行为,则向妳的应用程序的项目文件( .pro )中加入以下指令:
CONFIG-=app_bundle
如果想要让事情尽可能地简单,并且只部署少数几个文件的话,那么,必须利用静态链接的库来编译妳的程序。
首先安装一个静态编译版本的Qt库。记住,妳无法使用插件,并且,必须以静态链接的方式来构建那些依赖库,例如图片格式、SQL驱动等等。
cd /path/to/Qt
./configure -static <other parameters>
make sub-src
可运行 configure -help,以了解 有哪些可用的选项。
当妳完成静态编译Qt的过程之后,下一步就是,重新生成makefile,并且重新构建程序。首先,我们必须切换到包含着该程序的目录:
cd /path/to/Qt/examples/tools/plugandpaint
然后 ,运行 qmake ,以 便 为该应用程序生成一个新的makefile 。然后 ,重新构建该应用程序,以产生出一个静态链接的程序:
make clean
qmake -config release
make
妳可能想要与发布版本的库链接起来,那么 ,妳可以在运行 qmake 的时候指定这一点。如果 妳安装了 Xcode Tools 1.5 或更高的版本,那么, 妳还可以利用 " 死代码剥离 "功能 来减小该二进制文件的尺寸。具体 做法就是, 在运行 qmake 的时候,除了 -config release 参数之外,还传入另一个参数 LIBS+= -dead_strip 。
现在,如果所有代码 都正确的编译及链接通过了,那么, 我们应当已经得到一个可以部署的 plugandpaint.app 应用程序包了。试试 ,将这个应用程序包安装到 一个未安装Qt 或任何Qt 应用程序的OS X 系统上。
可使用 otool 来检查妳的程序还会链接到哪些其它库:
otool -L plugandpaint.app/Contents/MacOs/plugandpaint
对于静态链接 的 Plug & Paint ,其输出应当是以下这样的:
plugandpaint.app/Contents/MacOS/plugandpaint:
/System/Library/Frameworks/Carbon.framework/Versions/A/Carbon
(compatibility version 2.0.0, current version 128.0.0)
/System/Library/Frameworks/QuickTime.framework/Versions/A/QuickTime
(compatibility version 1.0.0, current version 10.0.0)
/usr/lib/libz.1.dylib
(compatibility version 1.0.0, current version 1.2.3)
/System/Library/Frameworks/ApplicationServices.framework/Versions/A/ApplicationServices
(compatibility version 1.0.0, current version 22.0.0)
/usr/lib/libstdc++.6.dylib
(compatibility version 7.0.0, current version 7.3.0)
/usr/lib/libgcc_s.1.dylib
(compatibility version 1.0.0, current version 1.0.0)
/usr/lib/libmx.A.dylib
(compatibility version 1.0.0, current version 92.0.0)
/usr/lib/libSystem.B.dylib
(compatibility version 1.0.0, current version 88.0.0)
如果妳在输出内容中发现了 Qt 库,那么,意味着,妳的机器上同时安装着动态和静态的Qt库。链接器永远会优先选项进行动态链接,其次才进行静态链接。如果妳只想使用静态库,那么,可采用以下某种方法:
•. 在链接程序时,将妳的 Qt动态 库 ( .dylibs )移动 到别的目录去,日后 再移动回来,
•. 或者 ,编辑 Makefile 文件,将针对Qt 库的链接代码修改成指向静态库的绝对路径。
例如,将以下代码:
-lQtGui
替换成:
/where/static/qt/lib/is/libQtGui.a
Plug & Paint 示例 由多个组件组成:核心程序 ( Plug & Paint ) ,以及, Basic Tools 和 Extra Filters 插件。由于 我们无法使用静态链接方式来部署插件,因此, 我们目前所产生的应用程序包是不完整的。 这个程序将能够运行起来,但是,由于缺失插件, 其功能会受限。 要想部署基于插件的程序,我们应当采用框架方式,这是OS X 独有的。
在这种方式中,需要确保,Qt运行时环境是随着该应用程序包正确地分发了,那些插件也要被安装到正确的位置,以使得程序能够找到它们。
以框架方式在程序中附带Qt,也有两种不同方法:
•.在妳的程序包中自带的私有框架。
•.标准框架(同时,使用安装的二进制程序中的Qt 框架作为替补)。
如果妳以某种特殊的方式构建了Qt,或者想要确保该框架的存在的话,则应当采用第一种手段。它就在妳自己放置Qt 框架的位置。
如果妳有多个Qt 程序,并且希望它们共用同一个Qt 框架,而不是各自附带一个,那么,妳应当采用第二种手段。
我们假设妳已经将 Qt 以框架的形式安装了, 而这是安装Qt 时的默认情况,它会被安装到/path/to/Qt目录。欲知更多关于如何 将Qt 构建为不带框架的形式,则阅读 用于OS X 的 Qt ——特殊问题 文档。
在安装时,会为那些框架设置其标识名字。链接 器( dyld )会利用这个名字来找到妳的应用程序所需要的库。
在以框架的形式构建了Qt 之后, 我们就可以构建 Plug & Paint 程序了。首先 ,我们切换到包含着该程序的目录:
cd /path/to/Qt/examples/tools/plugandpaint
现在 ,运行 qmake ,以便为该程序创建一个新的makefile。然后 ,重新构建,以产生出一个动态链接的可执行文件:
make clean
qmake -config release
make
这样就会构建出核心程序。然后,使用以下命令来构建那些插件:
cd ../plugandpaintplugins
make clean
qmake -config release
make
现在 ,针对那些Qt 框架运行 otool ,例如,对于Qt Gui:
otool -L QtGui.framework/QtGui
将会产生以下输出:
/path/to/Qt/lib/QtGui.framework/Versions/4.0/QtGui
(compatibility version 4.0.0, current version 4.0.1)
/System/Library/Frameworks/Carbon.framework/Versions/A/Carbon
(compatibility version 2.0.0, current version 128.0.0)
/System/Library/Frameworks/QuickTime.framework/Versions/A/QuickTime
(compatibility version 1.0.0, current version 10.0.0)
/path/to/Qt/QtCore.framework/Versions/4.0/QtCore
(compatibility version 4.0.0, current version 4.0.1)
/usr/lib/libz.1.dylib
(compatibility version 1.0.0, current version 1.2.3)
/System/Library/Frameworks/ApplicationServices.framework/Versions/A/ApplicationServices
(compatibility version 1.0.0, current version 22.0.0)
/usr/lib/libstdc++.6.dylib
(compatibility version 7.0.0, current version 7.3.0)
/usr/lib/libgcc_s.1.dylib
(compatibility version 1.0.0, current version 1.0.0)
/usr/lib/libmx.A.dylib
(compatibility version 1.0.0, current version 92.0.0)
/usr/lib/libSystem.B.dylib
(compatibility version 1.0.0, current version 88.0.0)
对于Qt框架 ,第一行 ( 也就是说, path/to/Qt/lib/QtGui.framework/Versions/4/QtGui (compatibility version 4.0.0, current version 4.0.1) ) 即成为该框架的标识名字,动态链接 器( dyld )会使用该名字。
可是 ,当妳部署该应用程序的时候,用户 的电脑上可能并没有在对应的位置安装有Qt 框架。因此 , 妳必须做点什么:或者 将框架放置到某个一致同意的位置去;或者, 将框架直接放置在应用程序包里。无论 妳采用哪种手段,都必须确保,那些框架 为自己返回了正确的标识名字, 而同时呢,应用程序 也正是以那些名字来寻找框架。幸运 的是,我们可以使用命令行工具 install_name_tool 来控制这些东西。
install_name_tool 有两种工作模式: -id 和 -change 。 -id 模式用于 库和框架, 可用来指定一个新的标识名字。 而 -change 模式呢,用来改变程序内部的那些路径。
让我们来试验一下, 将那些 Qt框架复制 到Plug & Paint 应用程序包中。查看 otool 对于 该应用程序包的输出内容, 我们会发现,必须 将 QtCore 和 QtGui 框架复制到应用程序包中。假设 ,我们现在正处在构建该应用程序包的目录中。
mkdir plugandpaint.app/Contents/Frameworks
cp -R /path/to/Qt/lib/QtCore.framework
plugandpaint.app/Contents/Frameworks
cp -R /path/to/Qt/lib/QtGui.framework
plugandpaint.app/Contents/Frameworks
首先 ,我们在应用程序包中创建一个 Frameworks 目录。 这是遵循 OS X应用程序惯例 的。然后 ,我们将那些框架复制到这个新目录中。由于框架 中包含着符号链接,所以,我们要使用 -R 选项。
install_name_tool -id @executable_path/../Frameworks/QtCore.framework/Versions/4.0/QtCore
plugandpaint.app/Contents/Frameworks/QtCore.framework/Versions/4.0/QtCore
install_name_tool -id @executable_path/../Frameworks/QtGui.framework/Versions/4.0/QtGui
plugandpaint.app/Contents/Frameworks/QtGui.framework/Versions/4.0/QtGui
然后 ,我们运行 install_name_tool 来为那两个框架设置标识名字。 -id 后面的第一个参数即为它的新名字,而第二个参数,就是我们要重命名的那个框架本身。字符串 @executable_path 是一个特殊的 dyld 变量 ,告知 dyld 要以该可执行程序为基准路径来查找文件。 新赋予的这些名字,表明, 这两个框架是位于此目录下的 Frameworks 目录中的。
install_name_tool -change path/to/Qt/lib/QtCore.framework/Versions/4.0/QtCore
@executable_path/../Frameworks/QtCore.framework/Versions/4.0/QtCore
plugandpaint.app/Contents/MacOs/plugandpaint
install_name_tool -change path/to/qt/lib/QtGui.framework/Versions/4.0/QtGui
@executable_path/../Frameworks/QtGui.framework/Versions/4.0/QtGui
plugandpaint.app/Contents/MacOs/plugandpaint
现在 ,动态链接器就知道去哪里寻找 QtCore 和 QtGui 了。 我们还必须确保,这个应用程序知道去哪里寻找对应 的库, 这就要用到 install_name_tool 的 -change 模式了。 这个过程本质上是字符串替换, 以匹配我们之前针对那两个框架所设置的标识名字。
最后 , QtGui 框架 还依赖 QtCore ,所以 , 我们还要记得改变 QtGui 中的引用:
install_name_tool -change path/to/Qt/lib/QtCore.framework/Versions/4.0/QtCore
@executable_path/../Frameworks/QtCore.framework/Versions/4.0/QtCore
plugandpaint.app/Contents/Frameworks/QtGui.framework/Versions/4.0/QtGui
做完这些之后,我们再次运行 otool , 将会发现,这个应用程序能够找到那些库了。
Plug & Paint 示例 中的那些插件,为它增加了额外的工作量。对于插件, 我们需要采取以下步骤:
•.将插件放置到应用程序包中,
•. 运行 install_name_tool ,以检查,那些插件是否使用了正确的库,
•.并且,确保该应用程序知道该去哪里寻找那些插件。
我们可以将插件放置在应用程序包中的任何位置,但是,最佳位置是,放置 在Contents/Plugins 目录中。 当我们构建Plug & Paint 中的插件时,基于 .pro 文件 中的 DESTDIR 变量,那些插件 的 .dylib 文件会被放置在 plugandpaint 目录下的 plugins 子目录中。 我们只需要将这个目录移动到正确的位置。
mv plugins plugandpaint.app/Contents
举个例子,如果我们针对 Basic Tools 插件的 .dylib 文件运行 otool ,则,会产生以下输出。
libpnp_basictools.dylib:
libpnp_basictools.dylib
(compatibility version 0.0.0, current version 0.0.0)
/path/to/Qt/lib/QtGui.framework/Versions/4.0/QtGui
(compatibility version 4.0.0, current version 4.0.1)
/System/Library/Frameworks/Carbon.framework/Versions/A/Carbon
(compatibility version 2.0.0, current version 128.0.0)
/System/Library/Frameworks/QuickTime.framework/Versions/A/QuickTime
(compatibility version 1.0.0, current version 10.0.0)
/path/to/Qt/lib/QtCore.framework/Versions/4.0/QtCore
(compatibility version 4.0.0, current version 4.0.1)
/usr/lib/libz.1.dylib
(compatibility version 1.0.0, current version 1.2.3)
/System/Library/Frameworks/ApplicationServices.framework/Versions/A/ApplicationServices
(compatibility version 1.0.0, current version 22.0.0)
/usr/lib/libstdc++.6.dylib
(compatibility version 7.0.0, current version 7.3.0)
/usr/lib/libgcc_s.1.dylib
(compatibility version 1.0.0, current version 1.0.0)
/usr/lib/libmx.A.dylib
(compatibility version 1.0.0, current version 92.0.0)
/usr/lib/libSystem.B.dylib
(compatibility version 1.0.0, current version 88.0.0)
于是,我们会看到,这个插件被链接到了它在构建时指定的那些Qt 框架。由于我们希望这些插件使用应用程序包中的那些框架,所以,我们就像改变该应用程序那样地改变它们。例如,对于Basic Tools 插件:
install_name_tool -change /path/to/Qt/lib/QtCore.framework/Versions/4.0/QtCore
@executable_path/../Frameworks/QtCore.framework/Versions/4.0/QtCore
plugandpaint.app/Contents/plugins/libpnp_basictools.dylib
install_name_tool -change /path/to/Qt/lib/QtGui.framework/Versions/4.0/QtGui
@executable_path/../Frameworks/QtGui.framework/Versions/4.0/QtGui
plugandpaint.app/Contents/plugins/libpnp_basictools.dylib
我们还必须修改代码 tools/plugandpaint/mainwindow.cpp , 做出 cdUp() 动作, 以确保, 该应用程序能够找到这些插件。 向 mainwindow.cpp 文件中加入以下代码:
#elif defined(Q_OS_MAC)
if (pluginsDir.dirName() == "MacOS") {
pluginsDir.cdUp();
}
#endif
tools/plugandpaint/mainwindow.cpp 中的那些额外代码,使得我们能够在Finder 中查看那些插件,如图所示。
我们还可以加入那些扩展了Qt 功能的插件,例如SQL 驱动 或图片格式。 我们只需要遵循插件文档中所指明的目录结构,并且 ,确保,它们 被包含在 QCoreApplication::libraryPaths ()中。 让我们依据前面说明过的过程来对图片格式插件做一下这个操作。
将Qt的图片格式插件复制到应用程序包中:
cp -R /path/to/Qt/plugins/imageformats
pluginandpaint.app/Contents/plugins
使用 install_name_tool 来将这些插件链接到应用程序包中的框架上去:
install_name_tool -change /path/to/Qt/lib/QtGui.framework/Versions/4.0/QtGui
@executable_path/../Frameworks/QtGui.framework/Versions/4.0/QtGui
plugandpaint.app/Contents/plugins/imageformats/libqjpeg.dylib
install_name_tool -change /path/to/Qt/lib/QtCore.framework/Versions/4.0/QtCore
@executable_path/../Frameworks/QtCore.framework/Versions/4.0/QtCore
plugandpaint.app/Contents/plugins/imageformats/libqjpeg.dylib
更新 tools/plugandpaint/main.cpp 中的代码,以寻找那些新的插件。 在构建完毕 QApplication 之后,加上以下代码:
QDir dir(QApplication::applicationDirPath());
dir.cdUp();
dir.cd("plugins");
QApplication::setLibraryPaths(QStringList(dir.absolutePath()));
首先 ,我们告知应用程序,只在这个目录中寻找插件。 在我们的项目中,我们希望该应用程序 只载入我们随应用程序包附带的那些插件 。如果 我们的程序是一个更大的Qt 程序包中的一部分,那么,我们应当使用 QCoreApplication::addLibraryPath ()。
警告: 在部署插件时,我们改变了源代码, 那会导致 在应用程序被重新构建时重置 为默认的标识名字。所以 ,妳需要再次使用 install_name_tool 来让妳的应用程序链接到正确的Qt 框架。
现在,妳应当可以将这个应用程序复制到另一个OS X 机器上,并且在不安装 Qt 的情况下运行它了。另外,还可以将这些框架移动到妳的应用程序包之外的某个目录中,以试验一下,这个应用程序还能不能运行。
如果妳将这些框架放置在应用程序包之外的另一个位置,那么,用来链接妳的应用程序的技巧是类似的;妳必须让该应用程序的那些框架就以下事情达成一致:到哪儿去寻找Qt库;到哪里去寻找插件。
当妳将妳的应用程序与Qt 链接 (无论是静态链接还是作为框架链接) 完了之后, 就可以发布它了。欲知更多信息 ,则阅读 工具工作 流指南 。
尽管部署应用程序的过程中有某些坑,但是,一旦妳搞清楚了各种各样的问题之后,就可以轻易地创建出让所有OS X 用户喜闻乐见的程序包了。
所有 的 Qt图形界面程序 , 都需要一个实现了Qt 5 中的 Qt平台抽象 (QPA)层的插件。对于 OS X ,这个平台插件的名字是 libqcocoa.dylib 。 这个文件,必须位于妳的发布目录中的某个特定的子目录(默认 是 platforms )。或者 ,也可以按照下面说明的方法来调整Qt 寻找插件时的搜索路径。
妳的应用程序可能还依赖着别的 Qt插件,例如JPEG图片格式插件 或 SQL驱动插件 。 请确保,在妳的应用程序包中带上 妳所需要的那些Qt 插件。 与平台插件类似的是,每种类型 的插件都必须放置在妳的发布目录中的特定子目录(例如 imageformats 或 sqldrivers )中。
在部署一个使用Qt WebKit 来显示互联网上的HTML 页面的应用程序的时候,必须包含所有的文本解码器插件,以支持尽可能多的HTML 编码。
Qt 插件 的搜索路径( 以及一些其它的路径 )是硬编码到 QtCore 库中的。默认情况 下,第一 个插件搜索路径会被硬编码为 /path/to/Qt/plugins 。但是,使用 预定义的路径,有某些缺点。例如, 在目录机器上,它们可能不存在。所以 ,妳必须采用多种替代手段 ,来确保能够找到Qt 插件:
•. 使用 QApplication::addLibraryPath () 或 QApplication::setLibraryPaths () 。
•. 使用 一个第三方安装工具来改变 QtCore 库中硬编码的路径。
如何创建Qt插件 文档 中说明了一些问题,当妳 在构建并部署针对Qt 应用程序的插件时,需要注意 这些问题。
妳可以使用 otool 来检查, 妳的应用程序链接到了哪些库。运行 它的时候,要提供应用程序的路径作为参数:
otool -L MyApp.app/Contents/MacOS/MyApp
与编译器相关的库,通常不需要随应用程序一起发布。但是,部署应用程序的时候有多种方式,因为,在 OS X 上,Qt可采用多种方式来配置、构建及安装。一般来说,妳的目标能够帮助决定要采用什么方式来部署该应用程序。最后小节,说明了一些在部署应用程序的过程中要注意的事项。
Qt 5应用程序可在OS X 10.6 (Snow Leopard)及更高版本上构建及部署。这是通过 弱链接 来实现的。 在 弱链接 功能中,Qt会检测,当前所运行的电脑中,特定新版本的OS X 所加入的函数是否可用。这样,就使得,在运行于新版本的OS X 中的时候,Qt会使用新的特性,而同时,在旧版本的系统中,仍能保持兼容。
欲知更多关于OS X 上的交叉开发问题的信息,则访问 苹果公司 的开发者网站 。
链接 器被设置为与所有的OS X 版本相兼容,因此, 妳必须改变 MACOSX_DEPLOYMENT_TARGET 环境变量,以使得 弱链接 为妳的应用程序服务。 妳可以将以下代码:
QMAKE_MACOSX_DEPLOYMENT_TARGET = 10.3
加入到.pro文件中,然后,qmake就会做好后面的事。
欲知更多关于C++运行 时环境的信息,则访问 苹果公司 的开发者网站 。
苹果机部署工具位于QTDIR/bin/macdeployqt。它被设计于,自动地完成以下任务:创建一个以私有框架的形式包含Qt 库的可部署的应用程序包。
苹果 机部署工具也会依据以下规则 来部署那些Qt 插件(除非指定 了 -no-plugins 选项 ):
•.平台插件一定会部署。
•.插件的调试版本不会被部署。
•.设计师插件不会被部署。
•.图片格式插件一定会部署。
•.打印支持插件一定会部署。
•. 如果 该应用程序使用了 Qt SQL 模块,则 SQL驱动插件 会被部署。
•. 如果 该应用程序使用了 Qt Script 模块,则脚本( Script )插件会被部署。
•. 如果 该应用程序使用了 Qt SVG 模块,则 SVG图标插件 会被部署。
•.辅助功能插件一定会被部署。
要想在应用程序包中包含某个第三方库,则,在创建该应用程序包之后,手动将那个库复制到应用程序包中。
macdeployqt 支持 以下选项:
选项 |
说明 |
-verbose=<0-3> |
0 = 无输出,1 = 输出错误/警告(默认),2 = 输出一般信息,3 = 输出调试信息 |
-no-plugins |
跳过插件部署过程 |
-dmg |
创建一个.dmg磁盘镜像 |
-no-strip |
不要对二进制程序运行'strip' |
-use-debug-libs |
使用调试版本 的框架及插件进行部署 (隐匿设置 -no-strip ) |
-executable=<path> |
让指定的可执行程序也使用这里部署的框架 |
-qmldir=<path> |
针对指定路径下的那些.qml文件部署导入库(imports) |
海藻
海藻
海藻
Your opinionsHxLauncher: Launch Android applications by voice commands