赞
踩
作为ios开发员,打包是家常便饭啦…
所以,把复杂的流程简单化,有助于减轻自己的工作量,还能有效的防止问题发生…最重要的,没那么快秃顶!
1.编译unity生成xcode工程
2.部署更新ios打包工程
3.编译ios工程
4.签名生成包
我们一步一步来
unity APP 里面提供的方法进行脚本调用,只用在脚本里写好打包的类,参数,,,只用对应unity 版本的APP 调用该类就行。
using System.Collections; using System.IO; using UnityEditor; using UnityEngine; using System.Collections.Generic; using System; class ProjectBuilderIOS : ProjectBuilderAndroid { protected new static void InitSetting() { ApplicationPath = Application.dataPath.Replace("/Assets", ""); Scenes = FindEnabledEditorScenes(); BuildTarget = BuildTarget.iOS;//设置为发布平台ios ScriptingImplementation = ScriptingImplementation.IL2CPP;//设置打包模式为IL2CPP BuildOptions = BuildOptions.AcceptExternalModificationsToPlayer; OutputPath = ApplicationPath + "/IOSProject";//导出ios工程相对路径 PlayerSettings.applicationIdentifier = "com.dddd.aaa";//bundleid PlayerSettings.bundleVersion = "0.0.1";//版本号 PlayerSettings.productName = "il2cpp"; EditorUserBuildSettings.symlinkLibraries = false; } static void BuildForIosProjectIl2Cpp() { InitSetting(); doBuild(); } protected static void doBuild() { PlayerSettings.SetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup, "HOTFIX_ENABLE"); if(ScriptingImplementation == ScriptingImplementation.Mono2x) //Mono2x包才打开混淆 { Beebyte.Obfuscator.OptionsManager.SetObfuscatorEnable(true); }else if(ScriptingImplementation == ScriptingImplementation.IL2CPP) { Beebyte.Obfuscator.OptionsManager.SetObfuscatorEnable(false); } CSObjectWrapEditor.Generator.ClearAll(); CSObjectWrapEditor.Generator.GenAll(); DirectoryInfo dir = new DirectoryInfo(OutputPath+"/"+PlayerSettings.productName); if (dir.Exists) dir.Delete(true); if (TargetName != null) { dir.Create(); OutputPath += "/" + TargetName; } EditorUserBuildSettings.SwitchActiveBuildTarget(BuildTarget); EditorUserBuildSettings.exportAsGoogleAndroidProject = true; EditorUserBuildSettings.androidBuildSystem = AndroidBuildSystem.Gradle; PlayerSettings.SetPropertyInt("ScriptingBackend", (int) ScriptingImplementation, BuildTarget); EditorUserBuildSettings.buildScriptsOnly = true; Scenes = FindEnabledEditorScenes(); BuildPipeline.BuildPlayer(Scenes, OutputPath, BuildTarget, BuildOptions); Debug.Log("Application.buildGUID===========>" + Application.buildGUID); } }
使用shell能直接调用unity方法掉起工程里面对应的类和方法。这里我们为了整合流程,是在python脚本里调用shell去执行的。
#编译unity工程,导出iOS工程!
def exportIosProject():
print "编译unity工程..."
command = "/Applications/Unity2017.2.3f1/Unity.app/Contents/MacOS/Unity -quit -batchmode -projectPath /Users/admin/projectName -executeMethod ProjectBuilderIOS.BuildForIosProjectIl2Cpp"
unityCompile_statue = os.system(command)
unityCompile_statue>>=8
print unityCompile_statue
if unityCompile_statue==0 :
print "exportIosProject success"
else:
print "exportIosProject fail"
exit(0)
需要将文件拷贝到打包ios工程路径下,再更新文件引用(正常情况下,两次发布unity生成的ios工程,只有project->Classes->Native下的类会改变,如果有新接SDK,Libraries下文件才会改变)
ios导出工程文件夹意义
Classes:unity前端生成的类,Classes->Native下是前端自己代码生成的cpp文件,其他是unity自己的类,当更改unity版本时会改变
Libraries:unity前端接入的SDK文件。unity前端接的三方SDK里的ios相关SDK都会在这里面,包括unity自带的引擎ios实现,新接SDK或unity版本更新,该文件加会改变。
Data:unity前端生成的资源文件,底层不应该关心,直接覆盖并打到包里就行。
正常打包,只会跟新Classes->Native下的类,所以,脚本里只做文件覆盖,并更新Classes->Native下的类引用。
copytree(os.environ["BASKETBALL_PRO_DIR"]+"/IOSProject/Data", os.environ["BASKETBALL_PRO_DIR"]+"/iOSBuild/Basketball/Data")
copytree(os.environ["BASKETBALL_PRO_DIR"]+"/IOSProject/Libraries", os.environ["BASKETBALL_PRO_DIR"]+"/iOSBuild/Basketball/Libraries")
copytree(os.environ["BASKETBALL_PRO_DIR"]+"/IOSProject/Classes/", os.environ["BASKETBALL_PRO_DIR"]+"/iOSBuild/Basketball/Classes/")
#################################################################################################################################################### #新加的编辑xcodeproj工程文件的代码 pbxproj_dir = '%s/%s/project.pbxproj'%(pro_dir,fileName) pro_dir_arr = pro_dir.split('/') arr_len = len(pro_dir_arr) pro_dir_arr[arr_len-1] = 'Classes/Native/' Native_dir = '/'.join(pro_dir_arr) Native_files = file_name(Native_dir) print Native_files for file in Native_files: data = '' uuid_f = ''.join(str(uuid.uuid4()).upper().split('-')[1:]) uuid_r = ''.join(str(uuid.uuid4()).upper().split('-')[1:]) if (isincludestr(pbxproj_dir, file)): print '%s is already included' % (file) continue while(isincludestr(pbxproj_dir, uuid_f)): uuid_f =''.join(str(uuid.uuid4()).upper().split('-')[1:]) while(isincludestr(pbxproj_dir, uuid_r)): uuid_r =''.join(str(uuid.uuid4()).upper().split('-')[1:]) buildfilesection_str = creat_buildfilesection(file, uuid_f, uuid_r) refsection_str = creat_refsection(file, uuid_r) print buildfilesection_str print refsection_str PBXBuildFile_line = getstrlineinfile(pbxproj_dir, '/* Begin PBXBuildFile section */') insertline(pbxproj_dir, PBXBuildFile_line ,buildfilesection_str) PBXFileReference_line = getstrlineinfile(pbxproj_dir, '/* Begin PBXFileReference section */') insertline(pbxproj_dir, PBXFileReference_line ,refsection_str) folder_line = getstrlineinfile(pbxproj_dir, '/* Native */ = {')+2 insertline(pbxproj_dir, folder_line ,('%s /* %s */,' %(uuid_r,file))) PBXSourcesBuildPhase_line = getstrlineinfile(pbxproj_dir, '/* Begin PBXSourcesBuildPhase section */')+4 insertline(pbxproj_dir, PBXSourcesBuildPhase_line ,('%s /* %s in Sources */,' %(uuid_f,file))) print PBXBuildFile_line print PBXFileReference_line print folder_line print PBXSourcesBuildPhase_line ###################################################################################################################################################
#获取项目的info配置文件 try: if os.path.exists(pro_dir+"/Info.plist"): plist=readPlist(pro_dir+"/Info.plist"); print pro_dir+"/Info.plist" print plist print plist['CFBundleVersion'] else: #这里需要注意工程目录下是否有该目录 plist=readPlist(pro_dir+"/"+target+"/Info.plist"); print pro_dir+"/"+target+"/Info.plist" print plist print plist['CFBundleVersion'] except InvalidPlistException,e: print "not a plist or plist invalid:",e #修改项目的info配置文件(修改了项目的游戏版本和编译版本号)
在编译之前需要先清理下xcode工程
def clean(dir,pro_name):
print "开始清理!"
print dir
print pro_name
command = "cd %s; xcodebuild -target %s clean "% (dir,pro_name)
os.system(command)
编译分为两种:
第一种build,这种打出来的包里面会有调试信息,包体会增大,建议测试包使用,
第二种是Archive,这种是比较好的打包方式,没有调试信息,发布包建议用这种方式。
推荐使用第二种
def build(dir,pro_name,build_config,code_sign): print '\033[1;31;40m' print "*******************************************************************编译!*********************************************************" print dir print pro_name print '\033[0m' command = "cd %s;xcodebuild -target %s -sdk iphoneos -configuration %s CODE_SIGN_IDENTITY='%s'" % (dir,pro_name,build_config,code_sign) print command iosBuild_status = os.system(command) iosBuild_status>>=8 print iosBuild_status if iosBuild_status==0 : print "ios project compile success" else: print "ios project compile fail" exit(0)
def buildForArchive(dir,pro_name): print '\033[1;31;40m' print "*******************************************************************编译!*********************************************************" print dir print pro_name print '\033[0m' ipa_out_put_archive = "%s/build/%s.xcarchive" % (dir,pro_name) command = "cd %s;xcodebuild archive -project %s.xcodeproj -scheme %s -archivePath %s" % (dir,pro_name,pro_name,ipa_out_put_archive) print command iosBuild_status = os.system(command) iosBuild_status>>=8 print iosBuild_status if iosBuild_status==0 : print "ios project compile success" else: print "ios project compile fail" exit(0)
和编译对应的,生成包的方式也有两种,
第一种build,对build出来的app签名生成ipa
第二种Archive,对Archive出来的.xcarchive文件签名生成ipa
def export(current_section,dir,build_config,pro_name,version,code_sign,code_profile): print '\033[1;31;40m' print "*******************************************************************打包!**********************************************************" print dir print build_config; print pro_name print version print '\033[0m' print code_sign print code_profile #设置ipa包的包名和存储位置 current_time=time.strftime("%Y%m%d%H%M",time.localtime(time.time())) ipa_out_put = os.path.join(sys.path[0],"pack/%s-%s-%s-%s-%s.ipa"%(current_time,version,pro_name,current_section,build_config)) print "打包ipa!输出到 %s" % ipa_out_put if not os.path.exists(os.path.join(sys.path[0],"pack")): os.makedirs(os.path.join(sys.path[0],"pack")) print "pack 文件夹不存在 新建一个" _appPath = "%s/build/%s" % (dir,build_config) _appName = getFileFromDir(_appPath,"app") command = "cd %s;xcrun -sdk iphoneos PackageApplication -v %s/build/%s/%s.app -o '%s' —sign '%s' —embed %s" % (dir,dir,build_config,_appName,ipa_out_put,code_sign,code_profile) print command os.system(command)
def exportForArchive(current_section,dir,build_config,pro_name,version): print '\033[1;31;40m' print "*******************************************************************打包!**********************************************************" print dir print build_config; print pro_name print version print '\033[0m' #设置ipa包的包名和存储位置 current_time=time.strftime("%Y%m%d%H%M",time.localtime(time.time())) ipa_out_put = os.path.join(sys.path[0],"pack/%s-%s-%s-%s-%s"%(current_time,version,pro_name,current_section,build_config)) print "打包ipa!输出到 %s" % ipa_out_put if not os.path.exists(os.path.join(sys.path[0],"pack")): os.makedirs(os.path.join(sys.path[0],"pack")) print "pack 文件夹不存在 新建一个" #获取项目的打包配置文件 pakegePlistPath = "%s/release.plist" % (dir) #开始打包 command = "cd %s;xcodebuild -exportArchive -archivePath %s/build/%s.xcarchive -exportPath %s -exportOptionsPlist %s" % (dir,dir,pro_name,ipa_out_put,pakegePlistPath) print command os.system(command) #将生成的IPA重命名 copyfile(ipa_out_put+"/"+pro_name+".ipa",ipa_out_put+".ipa") deletetree(ipa_out_put);
对此,整个打包流程已走完
其中有用到一些自己封装的python方法,贴在下面
def insertline(file_name, line_n, text): with open(file_name, 'r+') as fp: lines = [] for line in fp: lines.append(line) fp.close() lines.insert(line_n, '\n') lines.insert(line_n, text) s = ''.join(lines) with open(file_name, 'r+') as fp: fp.write(s) fp.close() def isincludestr(file_name, str): IsIncludeStr = False with open(file_name, 'r+') as f: for line in f.readlines(): # if(line.find(file) == 0): if file in line: IsIncludeStr = True break; return IsIncludeStr def getstrlineinfile(file_name,str): line_count = 0 with open(file_name, 'r') as f: for line in f.readlines(): line_count = line_count+1 if str in line: return line_count return 0 def file_name(file_dir): L = [] for root, dirs, files in os.walk(file_dir): for file in files: if os.path.splitext(file)[1] == '.cpp': L.append(file) return L def creat_refsection(f_name, uuid_ref): return '%s /*%s*/ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = %s; sourceTree = "<group>"; };' % ( uuid_ref, f_name, f_name) def creat_buildfilesection(f_name, uuid_file, uuid_ref): return '%s /*%s*/ = {isa = PBXBuildFile; fileRef = %s /* %s */; };' % (uuid_file, f_name, uuid_ref, f_name) def copytree(file,outdir): if os.path.exists(outdir): shutil.rmtree(outdir) if os.path.exists(file): shutil.copytree(file,outdir) def deletetree(outdir): if os.path.exists(outdir): shutil.rmtree(outdir) def copyfile(file,outdir): if os.path.exists(file): shutil.copy(file,outdir) def deletefile(file): if os.path.exists(file): os.remove(file) def getFileFromDir(dir,ext): items = os.listdir(dir) print items for names in items: if names.endswith(ext): (_target,_extension) = os.path.splitext(names) print names return _target
上面只列出了整个打包过程的关键部分,整个流程走通需要很多步骤,这里就不一一叙述了,这里只是给大家一个参考和交流。
期待你的点赞和关注!如有疑问,联系作者。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。