关于 iOS 批量打包的总结 关于 iOS 批量打包的总结 构建自动化打包脚本

 

如果你曾经试过做多 target 的项目,到了测试人员要测试包的时候,你就会明白什么叫“生不如死”。虽然 Xcode 打包很方便,但是当你机械重复打 N 次包的时候,就会觉得这纯粹是浪费时间的工作。所以这时候自动化打包就显得尤为重要(其实就算只有一个 target,就算使用 Xcode 打包很方便,也应该构建自动化打包,因为你可以节省大量时间)。

构建自动化打包脚本

xcodebuild

使用 xcodebuild -h 来看看 xcodebuild 到底是干啥的

 
 
1
2
3
4
5
6
7
8
9
.
.
.
]
]
showsdks
exportOptionsPlist
]
project

这里我只截取了 usage 部分,option 部分太多没有截取。

这里介绍几条毕竟常用的命令

1. xcodebuild -list …

xcodebuild -list [[-project ]|[-workspace ]] [-json]

usage: 输出 project 中的 targets 和 configurations,或者 workspace 中 schemes。
-project 和 -workspace 是输出指定内容,不输入默认输出当前目录下。-json 是以 json 格式输出。

example:

 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
list
:
:
XX
XXTests
 
:
Debug
Release
 
.
 
:
XX

2. xcodebuild -project …

xcodebuild [-project ] [[-target ]...|-alltargets] [-configuration ] [-arch ]... [-sdk [|]] [-showBuildSettings] [=]... []...

usage:

-project: 指定 project 名字,默认首个 project。

-target: 指定对应的 target ,默认首个 target。

-configuration: 选择Debug 或 Release,默认 Release,当然如果你有自定义的配置的,就应该选你配置的,上面 -list 中有输出。

-showBuildSettings: 显示工程的配置。

=: 修改工程的配置文件。

buildaction ... : 如下,默认为 build

 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.
:
 
is
.
 
root
.
 
s
root
.
 
the
.

example:

  • $ xcodebuild -project 你的项目名字.xcodeproj -target 你的 target 名字 -configuration release

这行命令表示编译 xx.xcodeproj 的 xx target。在 terminal 中会看到编译过程,如果成功最后会输出 ** BUILD SUCCEEDED **。最后会在当前目录下生成 build/Release-iphoneos/xx.app

  • $ xcodebuild -project 你的项目名字.xcodeproj -target 你的 target 名字 -configuration release -showBuildSettings

这行命令使用 -showBuildSettings 是不会 build 项目的,只是输出工程的配置。这里输出的的内容有(内容过多,只截取部分)

 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
:
build
NO
staff
rX
TsuiYuenHong
NO
NO
NO
Developer
AppleInternal
Documentation
Library
Tools
NO
NO
arm64
.

如果要修改配置文件,就直接最命令最后加上你要修改的内容。
例如在这行命令最后加上指定证书

  • $ xcodebuild -project 你的项目名字.xcodeproj -target 你的 target 名字 -configuration release PROVISIONING_PROFILE="你证书的id"

其中的字段是上面 -showBuildSettings 显示的字段,也可以看官网介绍

3. xcodebuild -workspace …

xcodebuild -workspace -scheme [-destination ]... [-configuration ] [-arch ]... [-sdk [|]] [-showBuildSettings] [=]... []...

除了 workspace 和 scheme 之外其余选项都和上条命令相同。

-workspace: 指定 workspace 名字,默认首个 workspace

-scheme: 指定对应的 scheme ,默认首个 scheme

4 . xcodebuild -exportArchive …

这里顺便介绍一下 archive 命令,因为在下面使用 PackageApplication 会出一个警告说推荐使用 -exportArchive。所以我们就来尝试一下使用 archive 来生成 app。

首先使用一下命令来生成 .xcarchive 文件
xcodebuild archive -workspace xx.xcworkspace -scheme xx -archivePath xx.xcarchive
可以看出添加上 archive 命令和最后加入 -archivePath 生成archivePath的路径即可。
然后该路径下会生成一个 xx.archivePath,里面包括三个文件,xx.app.dsym文件(可用于bugly等监控bug的平台),info.plist(保存打包的一些信息),还有我们的 xx.app 文件。

其次使用 -exportArchive 生成 ipa 包

xcodebuild -exportArchive -archivePath xx.xcarchive -exportPath xx -exportFormat ipa

-archivePath: xx.archivePath 的路径

-exportPath: 输出路径

-exportFormat: 生成类型,这里选择我们需要的 ipa

这样就利用我们的 xcodebuild 命令来生成 ipa 包

xcrun

这里也使用 xcrun 来生成 ipa 包即可

xcrun -sdk iphoneos PackageApplication build/Release-iphoneos/xx.app -o ~/Desktop/xx.ipa

但是,在 macos10.12 和 Xcode8 的环境下会出现一个警告

warning: PackageApplication is deprecated, use xcodebuild -exportArchive instead.

说明 PackageApplication 已经被弃用了。

不过其实这一步可以几乎等价于将 xx.app 放入一个 payload 的文件夹下然后压缩文件夹为 xx.ipa,当然这样做缺失一些信息,不过并不影响程序的运行。

初步小结

综上,我们有两种方法来生成我们需要的 ipa 包。

  1. 使用 xcodebuild 命令来编译我们的项目生成 app,然后再用 xcrun 将 app 转 ipa。
  2. 使用 xcodebuild archive 命令来直接生成我们需要的 ipa。

虽然现在网上几乎都是使用 xcodebuild + xcrun 来来生成 ipa 包,不过既然官方说 PackageApplication is deprecated,那还是推荐使用第二种方法,一步到位。

自动化打包正式开始

这里从我工作室的一个项目切入,这个项目需要最终生成 18 个 ipa 包,但是他们几乎是共用一套代码的,不同的地方在于bundleName/bundleDisplayName/bundleid 等,以及一些资源文件的不同,例如 icon 等。所以可想而知如果选择手动打包的痛苦,并且当你打包到一半发现某个地方错了要重新打包 ……

这里说一下自动化打包1.0解决思路:

  1. 使用命令 defaults write 来修改项目中的 plist 文件,来达到修改 bundleName/bundleDisplayName/bundleid… 的目的。
  2. 使用命令 cp 来替换资源文件。
  3. 使用 xcodebuild -workspace .. 编译出 app 包。
  4. 使用 xcrun ... 生成 ipa 。

这是我最开始想到的思路,最终运行时间大概为每个包2.5m(时间主要浪费在编译),然后一套下来也要半个多小时。虽然比起手动打快了不少,但还是太慢了。毕竟自动化的目的不仅仅是自动,还要速度。

既然问题出在编译上,那我的思路就往编译一次多次使用这个方向上面思考。然后想到了既然只是资源文件和plist的不同,没有涉及到代码的更换(不过这个项目后期不同 app 会执行不同一套代码,不过也有解决办法),这里就出现了自动化打包2.0的版本。

  1. 使用 xcodebuild -workspace .. 编译出 app 包。
  2. 使用命令 defaults write 来修改项目中的 plist 文件,来达到修改 bundleName/bundleDisplayName/bundleid… 的目的。
  3. 使用命令 cp 来替换资源文件。
  4. 重签名 codesign -f -s "iPhone Distribution: xx co., LTD" --entitlements $Entitlements $ipaPath/Payload/YouXiaoYun.app
  5. 使用 xcrun ... 生成 ipa 。

和1.0大致相似,不过并不是每次生成 ipa 都需要编译一次。而是编译一次,然后直接修改 app 下内容,不过这里会出现签名错误的问题,因为在编译的最后会用证书帮 app 签名,如果你直接替换资源然后就生成 ipa 的话会导致 ipa 无法安装。

那这时候神奇的重签名技术就出来(重签名用在正途上的真少见…hhhh,关于重签名的文章 google 一下就会很多),使用 codesign 命令就可以帮修改过资源的 app 重签名。
最终使用2.0的时间基本是在5-6分钟左右。果然能机器完成的工作绝对不要手动完成,从半天到30分钟到最后的6分钟,节省下来的时间可以让你学习到更多。

上面说到如果不同 app 间会用到不同的代码。例如 app A 里面的 title 叫 A 部门,app B 里面 title 又叫 B 部门,这样就不会通过命令行直接修改到代码,不过我想到的是维护一个 plist 文件,plist 文件可以这样设计的,每个不同 app 的 bundleName 都设置字典的键,然后字典下就可以是你自定义的内容。然后每次启动 app 就根据 bundleName 来寻找对应的字典,然后 title 就赋值为 plist 下 title 的值。如果不同代码就根据 code1 里面的值来 switch 不同的代码。

关于 iOS 批量打包的总结
关于 iOS 批量打包的总结
构建自动化打包脚本

最终代码

以下是完整的脚本文件,部分信息需要自己替换。
以下脚本适用于一次打 N 个包,适用情况:

  1. 可以替换 bundle 信息
  2. 替换音频图片资源
  3. 可以执行不同代码
  4. 生成相应的plist文件
  5. 上传到蒲公英分发平台

当然也可以打一个包,适当删除某些代码即可。

 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# 1.Configuration Info
 
# 项目路径 需修改
 
# 打包生成路径 需修改
 
# 图标路径 需修改
 
# Provisioning Profile 需修改 查看本地配置文件
 
############# 重签名需要文件
# 以下文件需放在 ipaPath 路径下
plist
#############
 
# 版本号
 
)
 
# 蒲公英分发参数 不分发可忽略 默认不分发 下面的两个KEY是默认测试的网址对应KEY
0
 
# ---------------------------可选 如果需要替换 app 的 icon --------------------------------- #
 
#Schemes:
#        1.app1 app1Icon
#        2.app2 app2Icon
#        3.app3 app3Icon
 
# --------------------------------------------------------------------------------------- #
 
# 打包个数
#appPackNum[*]}
 
(
)
 
#appInfos[*]}
 
# Scheme Name
 
# Code Sign ID
 
# 生成 APP 路径
 
# 开始时间
`
 
# 创建打包目录
AllPack
 
# 本地存放全部 IPA 的路径
 
# 清除缓存
buildDir
 
# Build 生成 APP
then
else
fi
 
# 先创建 payload 文件夹
Payload
# 移动编译生成的 app 到桌面的 Payload 文件夹下
Payload
 
# 以下二选一
# 1.----全部打包----
#for (( i=0; i $plist_path
//xxxxxxxxxxxx/$appDownloadName.ipakinddisplay-imageurlhttps://xxxxxxxxxxxx/${appIconName}.pngkindfull-size-imageurlhttps://xxxxxxxxxxxx/${appIconName}.pngmetadatabundle-identifier你的bundidbundle-version$bundleVersionkindsoftwaretitle$appDownloadName
EOF
 
# 移动
appName
 
# 6.上传蒲公英分发平台
 
then
//www.pgyer.com/apiv1/app/upload
 
fi
 
done
 
# 清除无关文件
Payload
 
# 结束时间
`