最近需要對(duì)所有的iOS和Android工程通過(guò)jenkins持續(xù)集成励烦,軟件的編譯、打包泼诱、企業(yè)分發(fā)版的ipa發(fā)布都需要自動(dòng)化坛掠。在做ipa自動(dòng)化發(fā)布時(shí),需要拿到一個(gè)app里面相關(guān)的信息治筒,比如顯示名稱屉栓、版本號(hào)、bundle identifier等等耸袜。
然后在Jenkins構(gòu)建任務(wù)中通過(guò)增加Python腳本針對(duì)每個(gè)版本生成IPA,plist和包含itms安裝鏈接的網(wǎng)頁(yè)友多。
首先需要對(duì)iOS ipa包的結(jié)構(gòu)有些了解。
iOS IPA文件結(jié)構(gòu)
首先堤框,一個(gè)ipa其實(shí)就是一個(gè)zip文件改了后綴名域滥。如果你把ipa的后綴改回.zip,那么你就能通過(guò)各種解壓軟件直接解壓了胰锌。
解壓后的目錄結(jié)構(gòu)是:
Payload
|-- ...
|-- ...
|-- 軟件名.app
|
|-- ...
|-- ...
|-- Info.plist
其中,Info.plist就是軟件信息的所在藐窄。
但是別高興得太早资昧,這里的Info.plist并不是純文本文件重慢,而是一種被稱為Binary Plist的東西晤斩。之前在做iOS開(kāi)發(fā)時(shí)官紫,工程里面用的plist幾乎都是XML形式的純文本文件,所以一開(kāi)始我理所當(dāng)然地認(rèn)為這個(gè)也是純文本文件轿曙,結(jié)果在這里卡了半天。在命令行運(yùn)行man plist就能看到下面這段話:
The property list programming interface allows you to convert hierarchically structured combinations of these basic types to and from two formats: standard XML and an optimized, opaque binary format.
所以不能看到.plist結(jié)尾的文件就當(dāng)文本文件處理狸膏。
PYTHON中相應(yīng)的LIBRARY狡恬,biplist和plistlib
在知道ipa的結(jié)構(gòu)之后,就能知道需要用哪些Python library了棺亭,第一個(gè)是zipfile虎眨,這個(gè)庫(kù)能夠處理zip類型的文件,并且可以在不解壓的情況下讀取里面某個(gè)文件的內(nèi)容镶摘。另外一個(gè)是plistlib嗽桩。但是需要注意的是,在Python 3.4之前凄敢,這個(gè)庫(kù)是不支持 Binary Plist 的解析的碌冶。所以如果使用 Python 2.7 的話,需要使用三方庫(kù)biplist涝缝。
下面使用 Python 3.4 為例進(jìn)行解析扑庞。之所以選擇 Python 3.x 是因?yàn)?2.x 版本在處理 Unicode 和非 Unicode 混用時(shí)非常麻煩。而 Python 3.x 起拒逮,所有的 string 都使用 Unicode 編碼了罐氨,就跟 ObjC 中一樣。
import zipfile, plistlib, sys, re
def analyze_ipa_with_plistlib(ipa_path):
ipa_file = zipfile.ZipFile(ipa_path)
plist_path = find_plist_path(ipa_file)
plist_data = ipa_file.read(plist_path)
plist_root = plistlib.loads(plist_data)
print_ipa_info (plist_root)
def find_plist_path(zip_file):
name_list = zip_file.namelist()
pattern = re.compile(r'Payload/[^/]*.app/Info.plist')
for path in name_list:
m = pattern.match(path)
if m is not None:
return m.group()
def print_ipa_info(plist_root):
print ('Display Name: %s' % plist_root['CFBundleDisplayName'])
print ('Bundle Identifier: %s' % plist_root['CFBundleIdentifier'])
print ('Version: %s' % plist_root['CFBundleShortVersionString'])
if __name__ == '__main__':
args = sys.argv[1:]
if len(args) < 1:
print ('Usage: python3 ipaanalyze.py /path/to/ipa')
ipa_path = args[0]
analyze_ipa_with_plistlib(ipa_path)
首先使用zipfile將ipa文件打開(kāi)消恍,zipfile中岂昭,namelist()方法能夠列出里面包含的所有文件路徑,并返回一個(gè)list狠怨。根據(jù)ipa的結(jié)構(gòu)约啊,我們要找的Info.plist是Payload/軟件名字.app/Info.plist,所以這里使用一個(gè)正則表達(dá)式找到這個(gè)文件路徑佣赖。
ZipFile.read()這個(gè)方法恰矩,能夠在不解壓zip文件的情況下讀取里面的內(nèi)容,在Python 3.4中返回的是bytes類型憎蛤,再使用plistlib.loads()載入外傅。其中,loads()是 Python 3.4 才加入的新API俩檬,它接收一個(gè)bytes對(duì)象萎胰,將其解析成相應(yīng)的Python對(duì)象。
這樣棚辽,這個(gè)plist文件就變成了一個(gè)dict技竟,從而可以取到里面的內(nèi)容。
如果使用 Python 2.7屈藐, 則可以用biplist代替榔组,這個(gè)庫(kù)使用的是跟 Python 2.7 中plistlib相同的API,由于不想改服務(wù)器上默認(rèn)的python2.7熙尉,我使用的是biplist。代碼如下:
info_plist = workspace + "/build/Release-iphoneos/" + project + ".app/Info.plist"
print(info_plist)
try:
plist = readPlist(info_plist)
except (InvalidPlistException, NotBinaryPlistException), e:
print "Not a plist:", e
return -1
display_name = plist['CFBundleDisplayName']
identifier = plist['CFBundleIdentifier']
short_version = plist['CFBundleShortVersionString']
print ('Display Name: %s' % display_name)
print ('Bundle Identifier: %s' % identifier)
print ('Version: %s' % short_version)
如果不使用 Python搓扯,或者不想用這些類庫(kù)检痰,則可以通過(guò)一些外部程序來(lái)解決Binary Plist的讀取問(wèn)題。比如使用/usr/libexec/PlistBuddy或者是plutil锨推,這些都是 OS X 自帶的工具铅歼,通過(guò)它們,你可以讀取Plist中的指定項(xiàng)爱态,或者直接把plist變成JSON文件谭贪。
讀取ipa .app目錄下的圖片并還原
由于Xcode工程中圖標(biāo)的文件名和位置都不固定,拿到AppIcon需要分析工程文件和AppIcon.appiconset/Contents.json文件內(nèi)容锦担,感覺(jué)分析過(guò)程太復(fù)雜了俭识,容易出錯(cuò)誤,因此還是想通過(guò)分析編譯后的build/Release-iphoneos/myproject.app/包目錄下找洞渔。app中所有的圖片都在根目錄下套媚,但這些圖片文件已經(jīng)被xcode處理過(guò)了,在瀏覽器中顯示為空白磁椒,因此需要想辦法把圖片文件還原堤瘤。
在網(wǎng)上找到了一個(gè)python腳本,iPIn iPhone PNG Images Normalizer, 感覺(jué)這個(gè)代碼時(shí)間太久了浆熔,2007年寫的本辐,測(cè)試了下不能處理正確圖片。
在這篇文章的評(píng)論里面發(fā)現(xiàn)一個(gè)鏈接https://gist.github.com/urielka/3609051医增, 應(yīng)該是個(gè)2012年的改進(jìn)版本慎皱,看下面的評(píng)論說(shuō)是可用,實(shí)驗(yàn)了下果然可以叶骨。圖標(biāo)問(wèn)題解決了茫多,處理后的圖片可以在瀏覽器正常顯示了。