macOS(OSX10.14, Xcode10.3)发布Qt(5.12.9)程序
1. 申请应用开发证书与发布证书
2. 应用的编译、签名、动态库打包
2.1 设置Qt编译环境
#查看能否使用qmake
qmake -v
#如果不能执行qmake,则在/etc/profile增加以下内容
export QTDIR=/Applications/Qt5.12.9/5.12.9/clang_64
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$QTDIR/lib
export PATH=$PATH:$QTDIR/bin
2.2 Qt工程转化为Xcode工程(确保Qt工程编译和运行都没问题再做转换)
- 在Xcode上面编译Qt工程可以设置签名、沙盒还有其他发布信息,避免未知和繁琐的命令行操作
qmake xxx.pro -spec macx-xcode
# 然后在.pro文件目录下可以看到Xcode工程配置文件了
2.2 应用图标设置
2.2.1 创建.iconset文件夹
-
把16x16至512x512及其2x的png图片放置这个文件夹
-
使用转换脚本
#!/bin/bash # 注意app.iconset里面的图片命名必须是icon_NxN.png或icon_NxN@2x.png sips -z 16 16 ./icons/app_icon16x16@1x.png --out ./app.iconset/icon_16x16.png sips -z 32 32 ./icons/app_icon16x16@2x.png --out ./app.iconset/icon_16x16@2x.png sips -z 32 32 ./icons/app_icon32x32@1x.png --out ./app.iconset/icon_32x32.png sips -z 64 64 ./icons/app_icon32x32@2x.png --out ./app.iconset/icon_32x32@2x.png sips -z 64 64 ./icons/app_icon64x64@1x.png --out ./app.iconset/icon_64x64.png sips -z 128 128 ./icons/app_icon64x64@2x.png --out ./app.iconset/icon_64x64@2x.png sips -z 128 128 ./icons/app_icon128x128@1x.png --out ./app.iconset/icon_128x128.png sips -z 256 256 ./icons/app_icon128x128@2x.png --out ./app.iconset/icon_128x128@2x.png sips -z 256 256 ./icons/app_icon256x256@1x.png --out ./app.iconset/icon_256x256.png sips -z 512 512 ./icons/app_icon256x256@2x.png --out ./app.iconset/icon_256x256@2x.png sips -z 512 512 ./icons/app_icon512x512@1x.png --out ./app.iconset/icon_512x512.png sips -z 1024 1024 ./icons/app_icon512x512@2x.png --out ./app.iconset/icon_512x512@2x.png
2.2.2 使用Qt添加Icon
-
将app.iconset里面的图片合成一个.icns图片
iconutil -c icns app.iconset -o app.icns
-
在.pro文件设置应用图标
# 这里app.icns在同一个目录 ICON = app.icns
这个方法Qt内编译是有效的,但转换成Xcode工程就不行了
2.2.3 使用Xcode添加Icon
- 将app.iconset内的图片复制到.pro文件所在的目录/项目名/Images.xcassets/AppIcon.appIconset/内
- 在Xcode设置的General内找到App Icons,默认是Use asset Catalogs,点击它会提示Migrate app icons to an asset catalog,再点击Migrate就可以看到左侧Resource目录多出一项Images.xcasset
- 点击Images.xcasset,再点击AppIcon,就可以看到刚才放进去的图片,现在要做的是把图片拖拽到对应的位置,然后编译就可以看到图标出现在程序坞了
2.3 Xcode设置(点击屏幕左侧的项目名)
PROJECT
-
找到Packaging设置Product Bundle Identifier
com.company_name.product_name
TARGETS
2.3.1 General
-
Identity
在这里可以填写产品类型和版本信息
Bundle Identifier: com.company_name.product_name,必须要按这种格式,否则上传应用报错
Version: 必须要填写,否则上传应用报错
Build: 必须要填写,否则上传应用报错
-
Signing
选择Automatically manage signing,这样Xcode会显示这个应用需要哪些信息,选择team然后Xcode会自动帮你找到证书(前提是已安装开发和发布证书)
-
Deployment Info
选择最低的发布平台
-
App Icons
见2.2.3
-
Linked Frameworks and Libraries
在这里可以添加macOS系统自带的Frameworks,也可以添加Qt的framework
2.3.2 Capabilities
-
App Sandbox
现在所有macOS应用都需要沙盒,都选应用需要获取的访问权限。
编译之后会生成xxx.entitlements文件,后面给第三方动态库签名会用到
2.3.3 Build Settings
-
Architextures
选择Release编译模式,如果Qt工程没有debug版本的动态库最好设置scheme去掉debug编译和运行(标题栏-->Product-->Scheme-->Edit Scheme)
2.3.4 Build Phases
如果不想每次编译后手动执行脚本,可以在Build Phases添加打包和签名脚本,个人推荐这么做,因为编译后手动操作打包签名后上传应用会莫名其妙地出现签名错误问题
点击左上角+,添加Run Script
-
添加Qt动态库打包脚本(在Link Binary With Libraries之后)
/Applications/Qt5.12.9/clang_64/bin/macdeployqt $BUILT_PRODUCTS_DIR/$FULL_PRODUCT_NAME
-
添加Qt动态库签名脚本(所有编译操作之后)
find $BUILT_PRODUCTS_DIR/$FULL_PRODUCT_NAME/Contents/PlugIns -name ".dylib" | xargs /usr/bin/codesign --force --sign xxcertificationsxx --entitlements xxx.entitlements --timestamp=none find $BUILT_PRODUCTS_DIR/$FULL_PRODUCT_NAME/Contents/Frameworks -name ".framework" | xargs /usr/bin/codesign --force --sign xxcertificationsxx --entitlements xxx.entitlements --timestamp=none
添加了这个脚本每次编译前都清理一下(Product --> clean),另外这些签名方法是在编译信息里面找到的
2.3.5 Archive Post-actions (自定义生成Archive包操作)
使用Archive打包应用的优点在于比较容易在本地验证应用是否存在问题,能够在上传应用之前排除一些规范问题
-
设置Post-actions
在Product找到Scheme选项,在Scheme选项找到Edit Scheme,在Edit Scheme找到Archive,点击Archive右侧的三角形后会看到Post-actions,
勾选Post-actions,添加Run Script,把打包和签名的脚本写填上去
-
执行Archive前必须清理工程,否则$BUILT_PRODUCTS_DIR/$FULL_PRODUCT_NAME可能不会生效,因为在Release Build之后$BUILT_PRODUCTS_DIR/$FULL_PRODUCT_NAME会指向Release目录下的app
APP=$BUILT_PRODUCTS_DIR/$FULL_PRODUCT_NAME
/Applications/Qt5.12.9/clang_64/bin/macdeployqt $APP
find $APP/Contents/PlugIns -name ".dylib" | xargs /usr/bin/codesign --force --sign xxcertificationsxx --entitlements xxx.entitlements --timestamp=none
find $APP/Contents/Frameworks -name ".framework" | xargs /usr/bin/codesign --force --sign xxcertificationsxx --entitlements xxx.entitlements --timestamp=none
2.3.6 Xcode提示的设置
如果Xcode提示设置就跟着提示设置
2.4 打包Qt动态库与第三方动态库
2.4.1 使用macdeployqt打包程序动态库
#切换到macdeployqt所在的目录,或者在qt工程中右击Open With Terminal,选择Build Enviroment,执行
macdeployqt xxx.app
#把其他第三方动态库也复制过来,我放置的目录是xxx.app/Content/Frameworks
#如果缺少rwx相关的权限要记得补充
2.4.2 修改动态库链接路径
如果第三方动态库路径不在xxx.app/Content内,那么应该要修改一下动态库路径
# 查看可执行文件的动态库依赖路径
otool -L xxx.app/Content/MacOS/xxx
# 查看动态库路径
otool -D xxx.dylib
# 修改路径(我的动态库都放在Frameworks这个文件夹中,对应的标识是@rpath)
sudo install_name_tool -id @rpath/xxx.dylib xxx.dylib
2.4.3 动态库签名
安装好证书以后,签名可以在Xcode或者钥匙串都能找到
- Qt Frameworks签名
#切换到Frameworks路径,使用macdeployqt打包以后在xxx.app/Content/目录下可找到
find . -name "*.framework" | codesign --entitlements xxx.entitlements -s “Mac Developer:xxxxxx"
- 动态库签名
# 切换到动态库目录
find . -name "*.dylib" | codesign --entitlements xxx.entitlements -s “Mac Developer:xxxxxx"
2.4.4 检查应用是否已签名
如果应用已被Xcode编译运行成功,一般来说已经完成签名了,可执行以下命令检查
codesign --display --verbose=4 xxx.app
如果输出签名信息,则说明签名成功了
3. 应用发布
3.1 准备工作
- 确保已在https://deleloper.apple.com/account/resources/identifiers/list 增加Identifiers
- 确保已在https://appstoreconnect.apple.com/apps增加app
3.2 使用Archive发布
这个方法会另外编译工程,所以在Release文件夹下执行macdeployqt、签名动态库无效,Archive发布的优点是自动签名与上传应用之前验证应用
-
确保2.3.5的Post-actions脚本能正确执行
-
执行Archive (Xcode标题栏 --> product --> Archive)
-
检查Archive包
Archives编译生成的执行文件在/Users/xxx/Library/Developer/Xcode/Archives
- 查看动态库和framwork是否完全打包
- 检查Archive包内的程序能否正常启动
-
验证Archive包
找到app目录执行macdeployqt打包然后给动态库签名,最后在Archives窗口使用Distribute App 下方的Validate App验证app
-
上传Archive包
生成Archive文件之后,在屏幕右侧可以看到 Distribute App ,填好描述内容,点击它可以看到发布方法,第一个方法是可以直接发布到Mac App Store
3.3 使用Application loader发布
不清楚productbuild可以man productbuild,文档结尾有现成的例子
# 打包Release目录下的app
productbuid --component build/Release/Sample.app /Applications --sign "3rd Party Mac Developer Installer: xxx" Product.pkg
然后使用Xcode的Application loader上传
4. 遇到的问题
-
第三方动态库验证不通过,Couldn't find platform family in Info.plist CFBundleSupportedPlatforms or Mach-O LC_VERSION_MIN for xxx.dylib
我使用的libusb、quazip编译出来动态库都是只有一个动态库并没有Info.plist等信息,在本地使用这些动态库运行程序没问题,打包程序并在其他机器运行也没有问题,问题就出现在应用验证。在网上看了好多方法,感觉可行的方法是:不使用第三方动态库,下载第三方库的源码直接引入到程序中调用。应用上架App Store之后,偶然发现macOS的应用其实可以选择不在App Store发布,如果不打算在App Store 发布用2.4和3.3的方法打包就足够了,这样就不会有动态库验证的问题。
-
手动使用脚本签名程序启动失败
在不知道Post-actions之前,我都是手动在Archive目录下给程序执行打包签名脚本,在Contents/MacOS目录下执行程序失败了,但在应用验证却通过了。直接将Release目录下已签名能正常启动的程序复制过来覆盖Archive目录原有的程序也不行。后来在Post-actions增加打包签名的脚本让Xcode自己执行就没问题了。
本人水平有限,也不是苹果开发专业户,此文仅作参考。
参考文章:
- https://blog.csdn.net/fengmm521/article/details/78446388?utm_medium=distribute.pc_relevant_bbs_down.none-task--2allfirst_rank_v2rank_v28-29.nonecase&depth_1-utm_source=distribute.pc_relevant_bbs_down.none-task--2allfirst_rank_v2rank_v28-29.nonecase
- https://blog.csdn.net/readyshowshow/article/details/102547633
- https://zhuanlan.zhihu.com/p/60499140
- https://www.apps121.com/2017/12/22/qtmacappstore/
- https://www.jianshu.com/p/5bf7795db50d
- https://www.cnblogs.com/hellovoidworld/p/4127576.html
- https://blog.csdn.net/weixin_43216130/article/details/89683110?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.control
- https://*.com/questions/29293840/how-to-use-iconutil-on-mac-to-generate-icns-file
- https://*.com/questions/30110757/xcode-error-when-uploading-to-app-store-no-suitable-application-records-were-f