Unity自動化構建之iOS打包(另有Android篇)

Jenkins持續集成Unity遊戲項目

Jenkins的安裝部署和配置

Jenkins中的Android打包任務設計

Jenkins中的iOS打包任務設計

Jenkins中的測試任務設計

iOS打包需要的流程

整個打包流程與Android打包是類似的。

難點主要集中在插入必要的FrameWork,修改Plist

在Unity5.x上Unity官方已經幫我們提供了一套API用來對Xcode工程的處理。並且提供了[PostProcessBuild]這個標籤來完成插入工作。

在Unity4.x上使用這套API可以去下載Unity官方提供的代碼 Unity-Technologies / XcodeAPI - Bitbucket

Unity導出前的準備

手上的項目分多個地區,所以在導出前會有不同的資源切換和資源刪除

代碼如下:

static void ExportiOSHFProject_Online() {n getCMDArgs();n PlayerSettings.defaultInterfaceOrientation = UIOrientation.AutoRotation;n PlayerSettings.productName = "xxxx"; //設置韓服的Namen //設置Deployment Targetn PlayerSettings.SetScriptingDefineSymbolsForGroup(BuildTargetGroup.iOS, "TWSDK;EXCLUDE_SHARESDK;KOREAN");n if (OnlySetScriptingDefineSymbols)n {n Debug.Log("本次打開Unity只是設置全局宏,打包請再次調用");n return;n }n string[] NeedToDel = {""};//指定要刪除的目錄n doDelWithRelativePath(NeedToDel);n _LanguageBar.ToTraditionForAntBuild(_LanguageBar.UNIRES_KOREA); //切換語言版本n ExportProject(ExportPath, BuildTarget.iOS, BuildOptions.None);n }n static void ExportProject(string path,BuildTarget target,BuildOptions options)n {nn if (path.Length != 0)n {n foreach (EditorBuildSettingsScene scene in EditorBuildSettings.scenes)n {n if (!scene.enabled) continue;n levels.Add(scene.path);n }nn float time = Time.realtimeSinceStartup;n AssetDatabase.Refresh();n tryn {n BuildPipeline.BuildPlayer(levels.ToArray(), path, target, options);n }n catch (System.Exception m)n {n Debug.LogError(m.Message);n }n time = Time.realtimeSinceStartup - time;n Debug.Log("打包完成,共計耗時" + time);n }nn }n

踩坑注意點

  • 1.OnlySetScriptingDefineSymbols

    這個是在命令行打包時候外部傳過來的值,為了判斷是來導出工程還是設置宏。

    本處這樣設置是因為,在一開始我的打包腳本也是用宏來區分執行切換哪部分資源的,

    但是後來出現了一個奇怪的問題,如果我剛剛Checkout出來的Unity默認宏是A的話, 但是我想打包的是B。

    我直接設置PlayerSettings.SetScriptingDefineSymbolsForGroup為B,

    確實非Editor和非[PostProcessBuild]標籤的腳本都被編譯了為B。

    但是在我Unity內部打包腳本的宏沒有切換,也就導致了我Unity腳本都切換了為B,但是我資源等切換都還是停留在A上,

    所以這裡設置為第一次打開Unity設置PlayerSettings.SetScriptingDefineSymbolsForGroup為B,然後關閉再打開Unity這樣所有的腳本都被切換到B了。

    這個問題我自己被坑了挺久,當然也有可能是自己的設計有問題,希望大家有更加好的解決辦法,或則是我的錯誤,希望大家能分享一下。

  • 2.BuildPipeline.BuildPlayer()

    options ios需要切換到BuildOptions.None,這個參數我也並沒有特別的明白,但是如果選擇其他或則與Android的類似,會出現些問題。

插入FrameWork和Plis

接下來肯定是大家最關注的點了,我把代碼直接貼出來吧,隱去了設置的的參數,宏因為是公司項目有些修改,可以根據自己的來修改。

public class AntXcodeProjectProcess : MonoBehaviour {nn [PostProcessScene]n public static void OnPostprocessScene()n {n n#if UNITY_ADSn AdvertisementSettings.enabled = true;n AdvertisementSettings.initializeOnStartup = false;n#endifn }n [PostProcessBuild(100)]n public static void OnPostprocessBuild(BuildTarget buildTarget, string path)n {n if (buildTarget == BuildTarget.iOS)n {nttt//plistntttstring plistPath = Path.Combine(path, "info.plist");ntttPlistDocument plist = new PlistDocument ();ntttplist.ReadFromFile (plistPath);n plist.root.SetString ("CFBundleDevelopmentRegion", "zh_CN");ntttplist.root.SetString("NSCameraUsageDescription", "直播相關");ntttplist.root.SetString("NSAppleMusicUsageDescription", "直播相關");nn#if TWSDK && !KOREANn plist.root.SetString("FacebookAppID", "");n plist.root.SetString("FacebookDisplayName","");n plist.root.CreateArray("CFBundleURLTypes").AddDict().CreateArray("CFBundleURLSchemes").AddString("");n string[] LSApplicationQueriesSchemesValueList = { "starcoin", "fbapi", "", "", "", "", "", "" ,"","","","","" ,""};n PlistElementArray LSApplicationQueriesSchemesArray = plist.root.CreateArray("LSApplicationQueriesSchemes");n for (int i = 0; i < LSApplicationQueriesSchemesValueList.Length; i++) {n LSApplicationQueriesSchemesArray.AddString(LSApplicationQueriesSchemesValueList[i]);n }n plist.root.SetString("NSPhotoLibraryUsageDescription", "允許授權後遊戲體驗會更豐富");nn#elif TWSDK && KOREANn plist.root.SetString("FacebookAppID", "");n plist.root.SetString("FacebookDisplayName", "");n plist.root.CreateArray("CFBundleURLTypes").AddDict().CreateArray("CFBundleURLSchemes").AddString("");n string[] LSApplicationQueriesSchemesValueList = { "starcoin", "fbapi", "", "", "", "", "", "" ,"","","","","" ,""};n PlistElementArray LSApplicationQueriesSchemesArray = plist.root.CreateArray("LSApplicationQueriesSchemes");n for (int i = 0; i < LSApplicationQueriesSchemesValueList.Length; i++) {n LSApplicationQueriesSchemesArray.AddString(LSApplicationQueriesSchemesValueList[i]);n }n plist.root.SetString("NSPhotoLibraryUsageDescription", "允許授權後遊戲體驗會更豐富");n#endifn PlistElementDict dictTransportSecurity = plist.root ["NSAppTransportSecurity"].AsDict ();ntttdictTransportSecurity.SetBoolean("NSAllowsArbitraryLoads",true);nn#if !EXCLUDE_SHARESDKn plist.root.CreateArray("LSApplicationQueriesSchemes").AddString("weixin"); // 微信分享ntttplist.root.CreateArray("CFBundleURLTypes").AddDict().CreateArray("CFBundleURLSchemes").AddString("");n#endifntttplist.WriteToFile (plistPath);n n //projectn string projPath = PBXProject.GetPBXProjectPath(path);n PBXProject proj = new PBXProject();n proj.ReadFromString(File.ReadAllText(projPath));n string target = proj.TargetGuidByName("Unity-iPhone");n#if TWSDK && !KOREANn proj.SetTargetAttributes("ProvisioningStyle", "Manual");//關閉自動證書管理n#elif TWSDK && KOREANn proj.SetTargetAttributes("ProvisioningStyle", "Manual");//關閉自動證書管理n#endifnn //add common framework startn proj.AddFrameworkToProject(target, "ReplayKit.framework", true);n proj.AddFrameworkToProject(target, "ImageIO.framework", true);n proj.AddFrameworkToProject(target, "Storekit.framework", false);n proj.AddFrameworkToProject(target, "JavaScriptCore.framework", true);n if (proj.ContainsFramework(target, "Metal.framework")){n proj.RemoveFrameworkFromProject(target, "Metal.framework");n proj.AddFrameworkToProject(target, "Metal.framework", true);n }n //add common framework endn proj.AddBuildProperty(target, "FRAMEWORK_SEARCH_PATHS", "$(SRCROOT)");n proj.AddBuildProperty(target, "FRAMEWORK_SEARCH_PATHS", "$(SRCROOT)/Libraries");nn string[] addBuildProperty = { "$(SRCROOT)/Libraries", "$(SRCROOT)" };n string[] removeBuildProperty = { ""$(SRCROOT)/Libraries"", ""$(SRCROOT)"" };n proj.UpdateBuildProperty(target, "LIBRARY_SEARCH_PATHS", addBuildProperty, removeBuildProperty);nn proj.AddBuildProperty(target, "OTHER_LDFLAGS", "-ObjC");nn //custom frameworl startnnnn#if SDKn proj.AddFileToBuild(target, proj.AddFile("/usr/bin/libsqlite3.tbd", "Frameworks/libsqlite3.tbd", PBXSourceTree.Sdk));n proj.AddFileToBuild(target, proj.AddFile("/usr/bin/libicucore.tbd", "Frameworks/libicucore.tbd", PBXSourceTree.Sdk));n#endifn#if SDKSDKn proj.AddFrameworkToProject(target, "Security.framework", false);n proj.AddFrameworkToProject(target, "Storekit.framework", false);n proj.AddFrameworkToProject(target, "SafariServices.framework", false);n proj.AddFrameworkToProject(target, "CoreData.framework", true);n proj.AddFrameworkToProject(target, "MobileCoreServices.framework", true);n proj.AddFrameworkToProject(target, "EventKit.framework", true);n proj.AddFrameworkToProject(target, "EventKitUI.framework", true);n proj.AddFrameworkToProject(target, "Social.framework", true);n proj.AddFrameworkToProject(target, "CoreTelephony.framework", true);n proj.AddFrameworkToProject(target, "MessageUI.framework", true);n proj.AddFileToBuild(target, proj.AddFile("/usr/bin/libsqlite3.tbd", "Frameworks/libsqlite3.tbd", PBXSourceTree.Sdk));n proj.AddFileToBuild(target, proj.AddFile("/usr/bin/libstdc++.tbd", "Frameworks/libstdc++.tbd", PBXSourceTree.Sdk));n#endifn //custom frameworl endnn //custom SDKFile startn#if SDKn XcodeDirectoryProcessor xdp = new XcodeDirectoryProcessor();n xdp.CopyAndAddBuildToXcode(proj,target,"XcodeFiles/SDK/",path,"SDKFiles");n xdp.CopyAndReplace("XcodeFiles/SDK/Unity-iPhone/Images.xcassets/AppIcon.appiconset",Path.Combine(path, "Unity-iPhone/Images.xcassets/AppIcon.appiconset"));nn#endifn#if TXWYSDK && !KOREANn Debug.Log("拷貝TXWYSDK文件夾");n XcodeDirectoryProcessor xdp = new XcodeDirectoryProcessor();n xdp.CopyAndAddBuildToXcode(proj,target,"XcodeFiles/TXWYSDK/",path,"SDKFiles");n xdp.CopyAndReplace("XcodeFiles/TWSDK/Unity-iPhone/Images.xcassets/AppIcon.appiconset",Path.Combine(path, "Unity-iPhone/Images.xcassets/AppIcon.appiconset"));n#elif TXWYSDK && KOREANn XcodeDirectoryProcessor xdp = new XcodeDirectoryProcessor();n xdp.CopyAndAddBuildToXcode(proj,target,"XcodeFiles/KOREASDK/",path,"SDKFiles");n xdp.CopyAndReplace("XcodeFiles/KOREASDK/Unity-iPhone/Images.xcassets/AppIcon.appiconset",Path.Combine(path, "Unity-iPhone/Images.xcassets/AppIcon.appiconset"));n#endifn //custom SDKFile endn File.WriteAllText(projPath, proj.WriteToString());nn }n }n}n

踩坑注意點

Plist地方沒有什麼的,都非常的簡單就直接根據需求進行設置就可以。

插入Framework就是全是坑了

  • 1.插入Framework

    如果你是直接在Unity5.x版本直接引用內部帶有的Api來插入的時候,如果版本比較低,最新的.tdb動態庫是插入不會成功的.

    即使在Xcode中會顯示,但是在編譯時候還是會通不過的。

    本人的Unity版本是5.2.4是必然不行的,所以可以使用Unity官方託管在Bitbucket的最新版本代碼來使用,下載它的代碼,然後修改命名空間直接使用就可以。 最新的已經支持了.tdb的動態庫添加,在添加動態庫時候請選擇PBXSourceTree.Sdk。 動態庫引用時候需要填寫路徑:」/usr/bin/「+「libicucore.tbd」

  • 2.自動證書管理

    這個我們的項目比較特殊,在國服的iOS我們有AppleID,有賬號密碼,所以在XcodeBuild時候可以直接進行簽名等。

    但是台服韓服等是有發行商給開發證書等,所以如果直接設置為自動管理證書無法簽名會報錯。

    因為這個原因我也自己研究了一下.xcproject,其實這個是個文件夾,裡面有配置文件可以直接修改。

    最早的時候,我是直接寫了一個Py腳本來修改這個,後來發現最新的API上有支持這個。

    >proj.SetTargetAttributes(「ProvisioningStyle」, 「Manual」);

    這樣既可修改為關閉自動證書管理,如果不修改這個ProvisioningStyle,Xcode會直接默認你是自動管理證書的。

  • 3.Iphone圖標管理

    Unity直接導出Xcode之後,在ICON界面,你會發現,自己的圖標並不齊全,所以直接提交AppStore可能會遇到直接被拒絕。

    所以可以自己新建工程,把ICON補齊了,然後把Images.xcassets/AppIcon.appiconset文件夾備份一下。

    每次打包時候直接

    >xdp.CopyAndReplace(「XcodeFiles/KOREASDK/Unity-iPhone/Images.xcassets/AppIcon.appiconset」,Path.Combine(path, 「Unity-iPhone/Images.xcassets/AppIcon.appiconset」));

  • 4.項目外的Framework引用

    我們常常會遇到我們的項目需要接入外部的SDK,所以他們會有自己的靜態庫等,需要我們引用。

    所以這個也可以在我們的Unity導出腳本中使用。

    自己如果直接通過AddFileToBuild去添加一個文件夾,你會發現這個文件夾永遠是藍色的而不是以一個Group的形式,被引入到Xcode工程中。

    但是如果說你先引入文件下的framework的話,你會發現文件夾已經直接被添加為Group了。

    所以直接封裝了XcodeDirectoryProcessor這個對文件夾的引用。

    添加代碼下載在這裡

  • 5.OC代碼修改

    我們的項目直接被解耦的比較好,所以不需要直接修改Xcode導出的代碼,直接添加中間bridge的代碼就好。

    事實上如果有這個需求,可以直接寫腳本對導出的OC代碼進行替換。

    理論上每次導出的OC代碼是一樣的,如果不同版本的就不保證一定相等。

簽名

我盟直接使用Jenkins作為框架,直接安准XcodeBuild的插件來完成簽名等工作,由於Jenkins直接都是配置一下就好了,基本沒有坑,我也是直接一次調整通過,所以就不再重複。

提交IPA

我們是導出Ipa提交到發行商的網上,所以直接寫腳本提交了,沒有什麼可以參考性,但是如果想直接提交到Appstore可以使用XcodeBuild但是我沒有經驗,應該也是比較簡單的配置。

總結

至此基於Jenkins的Unity自動化構建Android和iOS文章都已近寫完了。

從進公司到現在的一個半月多時間,全公司基本沒有人對此方面有經驗的情況下,通過自己一個人踩坑,一步步完成了整個流程。

對於我來說也是學到了許多,希望大家看完我的文章也能學到。

畢竟自己才剛剛畢業才疏學淺,文中可能出現比較多的錯誤,代碼比較簡陋,希望大家不要嫌棄。

最後做個小廣告~

喜歡的可以轉載下我的小文章

Blog:Lohanry

推薦閱讀:

微表面模型-PBR渲染管線的材質
Unity線性空間下移動設備上烘焙變暗問題處理
UWA GOT v1.1 | 支持本地管理深度測評、全新的UWA API、兼容Unity 2017.3
Unity載入模塊深度解析(網格篇)

TAG:Unity游戏引擎 |