我上家公司的主管,用Python写了一个自动化编译工具,用于一条命令编译出ipa,然后把ipa上传到公司的服务器,生成一个链接,可以直接下载,不明觉厉,所以我决定自己尝试写一个。有些事真是,你原本会以为很难,但当你下定决心去做的时候,其实就很简单了。
说明
其实自动化编译就是利用Xcode提供的命令行编译工具xcodebuild,可以查看xcodebuild的使用方法,如下所示:
我们使用xcodebuid archive和xcodebuild -exportArchive两个命令来archive和export文件,最终生成ipa。使用的命令如下所示:
xcodebuild archive [-workspace|-project] [-scheme] [-configuration] [-archivePath] [CODE_SIGN_IDENTITY] [PROVISIONING_PROFILE]
xcodebuild [-exportArchive] [-archivePath] [-exportPath] [-exportOptionsPlist]
相关工具
我们使用了几个工具:
- PlistBuddy: 一款Apple发布的plist编辑文件
- security: 一款解析provisioning profile的工具
PlistBuddy
PlistBuddy位置目录:/usr/libexec,该工具用于编辑plist文件。
- 获取值:/usr/libexec/PlistBuddy -c ‘Print [key]’ [plistFile]
- 设置值:/usr/libexec/PlistBuddy -c ‘Set :[key] [value]’ [plistFile]
- 添加值:/usr/libexec/PlistBuddy -c ‘Add :[key] [type] [value]’ [plistFile]
- 删除值: /usr/libexec/PlistBuddy -c ‘Delete : [key]’ [plistFile]
security
security是用于解析.mobileprovision文件的工具,其实这个工具我不知道怎么用,我只知道这一个用法,.mobileprovision文件位于”~/Library/MobileDevice/Provisioning Profiles”目录下,命令如下:
security cms -D -i [FilePath]
代码示例
整段代码如下所示:
#!/usr/bin/python
# -*- coding:utf-8 -*-
# Filename: compile.py
# Author: WangLuofan
import os;
import sys;
import json;
import re;
import stat;
import subprocess;
class PListOperation():
def __init__(self, path):
self.path = path;
def getValueForKey(self, key):
pipe = subprocess.Popen(["/usr/libexec/PlistBuddy", "-c", "Print " + key, self.path], stdout=subprocess.PIPE);
result, _ = pipe.communicate();
return result;
def setValueForKey(self, key, value):
subprocess.call(["/usr/libexec/PlistBuddy", "-c", "Set :" + key + " " + value, self.path]);
def addValueForKey(self, key, type, value):
subprocess.call(["/usr/libexec/PlistBuddy", "-c", "Add :" + key +" " + type + " " + value, self.path]);
def delValueForKey(self, key):
subprocess.call(["/usr/libexec/PlistBuddy", "-c", "Delete :" + key, self.path]);
def checkXcode():
XcodePath = "/Applications/Xcode.app";
if(os.path.exists(XcodePath)):
getXcodeInfo();
else:
print "请确认本机已经正确安装Xcode";
exit();
return ;
def getXcodeInfo():
plist = PListOperation("/Applications/Xcode.app/Contents/version.plist");
version = plist.getValueForKey("CFBundleShortVersionString");
if(version == None):
print "无法获取本机Xcode的版本信息";
else:
print "本机当前安装的Xcode版本: " + version;
return ;
def generateOptionPlist(configs):
content = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + os.linesep;
content += "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd\">" + os.linesep;
content += "<plist version=\"1.0\">" + os.linesep;
content += "<dict>" + os.linesep;
content += "</dict>" + os.linesep;
content += "</plist>" + os.linesep;
with open("option.plist", "w") as plistFile:
plistFile.writelines(content);
plistOper = PListOperation(os.path.join(os.path.abspath(os.curdir), "option.plist"));
if dict.has_key(configs, "useBitcode"):
value = configs["useBitcode"];
if(value == "yes" or value == "true"):
plistOper.addValueForKey("compileBitcode", "bool", "true")
else:
plistOper.addValueForKey("compileBitcode", "bool", "false");
else:
plistOper.addValueForKey("compileBitcode", "bool", "false");
if dict.has_key(configs, "exportMethod"):
plistOper.addValueForKey("method", "string", configs["exportMethod"]);
else:
plistOper.addValueForKey("method", "string", "development");
return ;
def setting_before_archive(configs):
ProjName = configs["ProjectName"];
pbxPath = ProjName + ".xcodeproj/project.pbxproj";
if(os.path.exists(pbxPath) == False):
print "工程配置不正确,请自行验证.";
return False;
infoPlist = "";
if(os.path.exists("info.plist")):
infoPlist = "info.plist";
elif(os.path.exists(ProjName + "-info.plist")):
infoPlist = ProjName + "-info.plist";
elif(os.path.exists(os.path.join(ProjName, "info.plist"))):
infoPlist = os.path.join(ProjName, "info.plist");
el