#if UNITY_IOS
using UnityEngine;
using UnityEditor;
using UnityEditor.Callbacks;
using System.IO;
using UnityEditor.iOS.Xcode;
using System.Collections.Generic;
using System.Text.RegularExpressions;
public static class BuildiOSPlayer
{
[PostProcessBuild]
static void OnPostProcessBuild (BuildTarget target, string pathToBuiltProject)
{
Debug.Log(pathToBuiltProject);
EditProj(pathToBuiltProject);
EditInfoPlist(pathToBuiltProject);
EditUnityAppController(pathToBuiltProject);
}
static void EditProj(string pathToBuiltProject)
{
string projPath = pathToBuiltProject + "/Unity-iPhone.xcodeproj/project.pbxproj";
PBXProject pbxProj = new PBXProject();
pbxProj.ReadFromFile(projPath);
string targetGuid = pbxProj.TargetGuidByName("Unity-iPhone");
//string debugConfig = pbxProj.BuildConfigByName(target, "Debug");
//string releaseConfig = pbxProj.BuildConfigByName(target, "Release");
pbxProj.SetBuildProperty(targetGuid, "GCC_ENABLE_OBJC_EXCEPTIONS", "YES");
//pbxProj.SetBuildPropertyForConfig(debugConfig, "GCC_ENABLE_OBJC_EXCEPTIONS", "YES");
//pbxProj.SetBuildPropertyForConfig(releaseConfig, "GCC_ENABLE_OBJC_EXCEPTIONS", "YES");
pbxProj.AddFrameworkToProject(targetGuid, "CoreTelephony.framework", true);
pbxProj.AddFrameworkToProject(targetGuid, "StoreKit.framework", true);
pbxProj.AddFrameworkToProject(targetGuid, "AVKit.framework", true);
pbxProj.AddFileToBuild(targetGuid, pbxProj.AddFile("usr/lib/libsqlite3.dylib", "Frameworks/libsqlite3.dylib", PBXSourceTree.Sdk));
pbxProj.AddFileToBuild(targetGuid, pbxProj.AddFile("usr/lib/libz.dylib", "Frameworks/libz.dylib", PBXSourceTree.Sdk));
//pbxProj.SetBuildProperty(target, "FRAMEWORK_SEARCH_PATHS", "$(SRCROOT)/Frameworks");
//pbxProj.AddBuildProperty(target, "FRAMEWORK_SEARCH_PATHS", "$(inherited)");
//pbxProj.AddBuildProperty(target, "OTHER_LDFLAGS", "-ObjC");
#region 添加资源文件(中文路径 会导致 project.pbxproj 解析失败)
string frameworksPath = Application.dataPath + "/Frameworks";
string[] directories = Directory.GetDirectories(frameworksPath, "*", SearchOption.TopDirectoryOnly);
for (int i = 0; i < directories.Length; i++)
{
string path = directories[i];
string name = path.Replace(frameworksPath + "/", "");
string destDirName = pathToBuiltProject + "/" + name;
if (Directory.Exists(destDirName))
Directory.Delete(destDirName, true);
Debug.Log(path + " => " + destDirName);
Utility.CopyDirectory(path, destDirName, new string[] { ".meta", ".framework", ".mm", ".c", ".m", ".h", ".xib", ".a", ".plist", ".org", "" }, false);
foreach (string file in Directory.GetFiles(destDirName, "*.*", SearchOption.AllDirectories))
pbxProj.AddFileToBuild(targetGuid, pbxProj.AddFile(file, file.Replace(pathToBuiltProject + "/", ""), PBXSourceTree.Source));
}
#endregion
pbxProj.WriteToFile(projPath);
}
static void EditInfoPlist(string filePath)
{
string path = filePath + "/Info.plist";
PlistDocument plistDocument = new PlistDocument();
plistDocument.ReadFromFile(path);
PlistElementDict dict = plistDocument.root.AsDict();
PlistElementArray array = dict.CreateArray("CFBundleURLTypes");
PlistElementDict dict2 = array.AddDict();
dict2.SetString("CFBundleURLName", PlayerSettings.bundleIdentifier);
PlistElementArray array2 = dict2.CreateArray("CFBundleURLSchemes");
array2.AddString(PlayerSettings.bundleIdentifier);
dict2 = array.AddDict();
dict2.SetString("CFBundleURLName", "weixin");
array2 = dict2.CreateArray("CFBundleURLSchemes");
array2.AddString(BabybusConst.WEIXIN_ID);
dict2 = array.AddDict();
dict2.SetString("CFBundleURLName", "");
array2 = dict2.CreateArray("CFBundleURLSchemes");
array2.AddString("QQ" + BabybusConst.QQ_ID.ToString("X"));
dict2 = array.AddDict();
dict2.SetString("CFBundleURLName", "");
array2 = dict2.CreateArray("CFBundleURLSchemes");
array2.AddString("tencent" + BabybusConst.QQ_ID);
#region quick action
string[] quickActions = { "Poem", "Pet", "Movie", "Telephone" };
string[] quickActionsIcon = { "PoemIcon", "PetIcon", "MovieIcon", "TelephoneIcon" };
//string[] icons = { "UIApplicationShortcutIconTypeBookmark", "UIApplicationShortcutIconTypeLove", "UIApplicationShortcutIconTypeCaptureVideo", "UIApplicationShortcutIconTypeFavorite" };
array = dict.CreateArray("UIApplicationShortcutItems");
for(int i=0; i<quickActions.Length; ++i)
{
dict2 = array.AddDict();
//dict2.SetString("UIApplicationShortcutItemIconType", icons[i]);
dict2.SetString("UIApplicationShortcutItemIconFile", quickActionsIcon[i]);
dict2.SetString("UIApplicationShortcutItemTitle", quickActions[i] + "Title");
dict2.SetString("UIApplicationShortcutItemType", quickActions[i]);
dict2.CreateDict("UIApplicationShortcutItemUserInfo");
//dict2.SetString("UIApplicationShortcutItemSubtitle", quickActions[i]);
}
#endregion
dict.SetString("CFBundleIdentifier", PlayerSettings.bundleIdentifier);
var assetInfos = Utility.DeserializeXmlFromFile<List<AssetInfo>>(Application.dataPath + "/Resources/配置/APP.xml");
array = dict.CreateArray("LSApplicationQueriesSchemes");
foreach (var assetInfo in assetInfos)
{
if (string.IsNullOrEmpty(assetInfo.bundleIdentifier4iOS))
array.AddString(assetInfo.extra);
else
array.AddString(assetInfo.bundleIdentifier4iOS);
}
plistDocument.WriteToFile(path);
}
static void EditUnityAppController(string pathToBuiltProject)
{
string unityAppControllerPath = pathToBuiltProject + "/Classes/UnityAppController.mm";
if (File.Exists(unityAppControllerPath))
{
string headerCode = "#include \"../Libraries/Plugins/iOS/SDKPlatformIOS.h\"\n" +
"#import <AVFoundation/AVAudioSession.h>\n\n";
string unityAppController = headerCode + File.ReadAllText(unityAppControllerPath);
Match match = Regex.Match(unityAppController, @"- \(void\)startUnity:\(UIApplication\*\)application\s+\{[^}]+\}");
if(match.Success)
{
string newCode = match.Groups[0].Value.Remove(match.Groups[0].Value.Length - 1);
newCode += "\n" +
" [[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryPlayback error: nil];\n" +
" [[AVAudioSession sharedInstance] setActive:YES error:nil];\n" +
"}\n\n" +
"- (void)application:(UIApplication*)application performActionForShortcutItem: (UIApplicationShortcutItem*)shortcutItem completionHandler: (void(^)(BOOL))completionHandler\n" +
"{\n" +
" [[SDKPlatform share] performActionForShortcutItem:shortcutItem];\n" +
"}";
unityAppController = unityAppController.Replace(match.Groups[0].Value, newCode);
}
File.WriteAllText(unityAppControllerPath, unityAppController);
}
}
}
#endif
public static void CopyDirectory(string srcPath, string dstPath, string[] excludeExtensions, bool overwrite = true)
{
if (!Directory.Exists(dstPath))
Directory.CreateDirectory(dstPath);
foreach (var file in Directory.GetFiles(srcPath, "*.*", SearchOption.TopDirectoryOnly).Where(path => excludeExtensions == null || !excludeExtensions.Contains(Path.GetExtension(path))))
{
File.Copy(file, Path.Combine(dstPath, Path.GetFileName(file)), overwrite);
}
foreach (var dir in Directory.GetDirectories(srcPath))
CopyDirectory(dir, Path.Combine(dstPath, Path.GetFileName(dir)), excludeExtensions, overwrite);
}
Paste_Image.png
using UnityEngine;
using UnityEditor;
using UnityEditor.Callbacks;
using System.IO;
using UnityEditor.iOS.Xcode;
using System.Collections.Generic;
using System.Linq;
using Babybus.Framework.Extension;
namespace Babybus.Builder
{
public class BuildiOSPlayer : BuildPlayerBase
{
public const string projectFrameworks = "./../ProjectFrameworks";
[PostProcessBuild]
static void OnPostProcessBuild(BuildTarget target, string pathToBuiltProject)
{
if (target != BuildTarget.iOS)
return;
Debug.Log("pathToBuiltProject = " + pathToBuiltProject);
EditProj(pathToBuiltProject);
EditInfoPlist(pathToBuiltProject);
if (Application.platform == RuntimePlatform.OSXEditor)
Xcode2IPA(pathToBuiltProject);
}
static void Xcode2IPA(string pathToBuiltProject)
{
var workspace = BabySystem.workspace;
Debug.Log(workspace);
var channel = currentPackChannelItem.Channel;
var ipaName = GetProductPath(currentPackChannelItem);
var ipaPath = workspace + ipaName;
var bashPath = Application.dataPath + "/BabyFramework/Shell/xcodebuild.sh";
ProcessHelper.StartProcess("/bin/chmod", "+x " + bashPath);
var xcodeVersion = EditorPrefs.GetString("JenkinsBuilder.xcodeVersion");
if (xcodeVersion == "")
xcodeVersion = "7.3.1";
var mobileprovision = EditorPrefs.GetString("JenkinsBuilder.mobileprovision");
mobileprovision = "\"" + mobileprovision + "\"";
ProcessHelper.StartProcess("/bin/bash", bashPath + " /Applications/Xcode_" + xcodeVersion + ".app " + workspace + pathToBuiltProject + " " + ipaPath + " " + mobileprovision);
}
private static List<string> GetBuildFiles(string path)
{
var directories = Directory.GetDirectories(path);
var files = Directory.GetFiles(path).ToList();
foreach (var directory in directories)
{
var name = Path.GetFileName(directory);
if (name == ".svn")
continue;
if (name == "Languages")
continue;
if (name == "AdPlatform" && !BabySystem.HasAD())
continue;
#if BABYBUS_SHARE
if (name == "WeChat" && !directory.Contains("UMSocial"))
continue;
#else
if (name == "UMSocial")
continue;
#endif
if (directory.EndsWith(".framework") || directory.EndsWith(".bundle") || directory.EndsWith(".xcassets"))
files.Add(directory);
else
files.AddRange(GetBuildFiles(directory));
}
return files;
}
static void EditProj(string pathToBuiltProject)
{
var projPath = pathToBuiltProject + "/Unity-iPhone.xcodeproj/project.pbxproj";
var pbxProj = new PBXProject();
pbxProj.ReadFromFile(projPath);
var targetGuid = pbxProj.TargetGuidByName("Unity-iPhone");
//string debugConfig = pbxProj.BuildConfigByName(target, "Debug");
//string releaseConfig = pbxProj.BuildConfigByName(target, "Release");
//pbxProj.SetBuildPropertyForConfig(debugConfig, "GCC_ENABLE_OBJC_EXCEPTIONS", "YES");
//pbxProj.SetBuildPropertyForConfig(releaseConfig, "GCC_ENABLE_OBJC_EXCEPTIONS", "YES");
pbxProj.SetBuildProperty(targetGuid, "ENABLE_BITCODE", "NO");
pbxProj.SetBuildProperty(targetGuid, "GCC_ENABLE_OBJC_EXCEPTIONS", "YES");
var sign = EditorPrefs.GetString("JenkinsBuilder.sign", "iPhone Distribution: BABYBUS CO., LIMITED (AMSTN4PRF9)");
pbxProj.SetBuildProperty(targetGuid, "CODE_SIGN_IDENTITY", sign);
var provision = EditorPrefs.GetString("JenkinsBuilder.provision", "f4f9a377-57f0-4e0d-81c0-00904ac17378");
pbxProj.SetBuildProperty(targetGuid, "PROVISIONING_PROFILE", provision);
pbxProj.SetBuildProperty(targetGuid, "CODE_SIGN_ENTITLEMENTS", "Libraries/BabyFrameWork/Babybus.entitlements");
pbxProj.SetBuildProperty(targetGuid, "GCC_C_LANGUAGE_STANDARD", "gnu99");
var definitions = "BB_APP_3D";
if (BabySystem.GetIsForKids())
definitions += " BB_PUBLISH_KIDS";
if (!BabySystem.HasAD())
definitions += " BABYBUS_NOAD";
#if BABYBUS_SHARE
definitions += " BABYBUS_SHARE";
#endif
pbxProj.SetBuildProperty(targetGuid, "GCC_PREPROCESSOR_DEFINITIONS", definitions);
pbxProj.AddFrameworkToProject(targetGuid, "AVKit.framework", true);
pbxProj.AddFrameworkToProject(targetGuid, "Security.framework", true);
pbxProj.AddFrameworkToProject(targetGuid, "ImageIO.framework", true);
pbxProj.AddFrameworkToProject(targetGuid, "AssetsLibrary.framework", true);
if (BabySystem.HasAD())
pbxProj.AddFrameworkToProject(targetGuid, "AdSupport.framework", true);
#if BABYBUS_TALK2KIKI
pbxProj.AddFrameworkToProject(targetGuid, "AddressBook.framework", true);//讯飞语言识别需要用到
#endif
pbxProj.AddFrameworkToProject(targetGuid, "EventKit.framework", true);
pbxProj.AddFrameworkToProject(targetGuid, "EventKitUI.framework", true);
pbxProj.AddFrameworkToProject(targetGuid, "CFNetwork.framework", true);
pbxProj.AddFrameworkToProject(targetGuid, "WebKit.framework", true);
pbxProj.AddFrameworkToProject(targetGuid, "CoreGraphics.framework", true);
pbxProj.AddFrameworkToProject(targetGuid, "CoreTelephony.framework", true);
pbxProj.AddFrameworkToProject(targetGuid, "CoreLocation.framework", true);
pbxProj.AddFrameworkToProject(targetGuid, "MessageUI.framework", true);
pbxProj.AddFrameworkToProject(targetGuid, "MediaPlayer.framework", true);
pbxProj.AddFrameworkToProject(targetGuid, "CoreMotion.framework", true);
pbxProj.AddFrameworkToProject(targetGuid, "StoreKit.framework", true);
pbxProj.AddFrameworkToProject(targetGuid, "CoreMedia.framework", true);
pbxProj.AddFrameworkToProject(targetGuid, "MobileCoreServices.framework", true);
pbxProj.AddFileToBuild(targetGuid, pbxProj.AddFile("usr/lib/libsqlite3.dylib", "libsqlite3.dylib", PBXSourceTree.Sdk));
pbxProj.AddFileToBuild(targetGuid, pbxProj.AddFile("usr/lib/libz.dylib", "libz.dylib", PBXSourceTree.Sdk));
pbxProj.AddBuildProperty(targetGuid, "OTHER_LDFLAGS", "-ObjC");
pbxProj.AddBuildProperty(targetGuid, "FRAMEWORK_SEARCH_PATHS", "$(SRCROOT)/Libraries/BabyFrameWork/**");
pbxProj.AddBuildProperty(targetGuid, "HEADER_SEARCH_PATHS", "$(SRCROOT)/Libraries/BabyFrameWork/**");
pbxProj.AddBuildProperty(targetGuid, "LIBRARY_SEARCH_PATHS", "$(SRCROOT)/Libraries/BabyFrameWork/**");
var framework = BabySystem.workspace + projectFrameworks;
var files = GetBuildFiles(framework);
var compileFlags = new List<string>()
{
"-fno-objc-arc"
};
string[] arcDirectories = { "/AFNetworking/", "/BabyBusPlayer/", "/HBRSAHandler/",
"/SVPProgressHUD/", "/UIKit+AFNetworking/", "/Masonry/",
"/UMSocial/"
};
foreach (var file in files)
{
var path = file.Replace("\\", "/");
var projectPath = "Libraries/BabyFrameWork/" + path.Replace(framework, "");
FileTools.CopyFileOrDirectory(path, pathToBuiltProject + "/" + projectPath, true);
var fileGuid = pbxProj.AddFile(projectPath, projectPath, PBXSourceTree.Source);
pbxProj.AddFileToBuild(targetGuid, fileGuid);
if (path.EndsWith(".a") || path.EndsWith(".m") || path.EndsWith(".mm") || path.EndsWith(".c") || path.EndsWith(".cpp"))
{
var hasCompileFlags = true;
foreach (var directory in arcDirectories)
{
if (path.Contains(directory))
{
hasCompileFlags = false;
break;
}
}
if (hasCompileFlags)
pbxProj.SetCompileFlagsForFile(targetGuid, fileGuid, compileFlags);
}
}
var languages = Directory.GetDirectories(framework + "/Languages");
foreach (var lprojPath in languages)
{
var lprojName = Path.GetFileName(lprojPath);
if (!lprojName.EndsWith(".lproj"))
continue;
if (BabySystem.IsArabicApp && lprojName != "ar.lproj" || !BabySystem.IsArabicApp && lprojName == "ar.lproj")
continue;
var language = "";
if (lprojName == "zh_CN.lproj" && BabySystem.IsLanguageSupported("zh"))
language = "zh";
if (lprojName == "zh_TW.lproj" && BabySystem.IsLanguageSupported("zht"))
language = "zht";
if (language == "")
{
language = lprojName.Remove(lprojName.Length - ".lproj".Length);
if (!BabySystem.IsLanguageSupported(language))
continue;
}
var projectPath = lprojName;
FileTools.CopyFileOrDirectory(lprojPath, pathToBuiltProject + "/" + projectPath, true);
File.WriteAllText(pathToBuiltProject + "/" + projectPath + "/InfoPlist.strings", "\"CFBundleDisplayName\"=\"" + BabySystem.GetProductName(language) + "\";");
var fileGuid = pbxProj.AddFolderReference(projectPath, projectPath, PBXSourceTree.Source);
pbxProj.AddFileToBuild(targetGuid, fileGuid);
}
pbxProj.WriteToFile(projPath);
}
static void EditInfoPlist(string filePath)
{
var path = filePath + "/Info.plist";
var plistDocument = new PlistDocument();
plistDocument.ReadFromFile(path);
var dict = plistDocument.root.AsDict();
var array = dict.CreateArray("CFBundleURLTypes");
var dict2 = array.AddDict();
dict2.SetString("CFBundleURLName", PlayerSettings.bundleIdentifier);
var array2 = dict2.CreateArray("CFBundleURLSchemes");
array2.AddString(PlayerSettings.bundleIdentifier);
var wxappid = BuildProductKeys.GetValue("ios_um_wxappid");
if (!string.IsNullOrEmpty(wxappid))
{
dict2 = array.AddDict();
dict2.SetString("CFBundleURLName", "weixin");
array2 = dict2.CreateArray("CFBundleURLSchemes");
array2.AddString(wxappid);
}
var qqappid = BuildProductKeys.GetValue("ios_um_qqappid");
if (!string.IsNullOrEmpty(qqappid))
{
dict2 = array.AddDict();
dict2.SetString("CFBundleURLName", "");
array2 = dict2.CreateArray("CFBundleURLSchemes");
array2.AddString("tencent" + qqappid);
dict2 = array.AddDict();
dict2.SetString("CFBundleURLName", "");
array2 = dict2.CreateArray("CFBundleURLSchemes");
array2.AddString("QQ" + int.Parse(qqappid).ToString("X"));
}
var sinakey = BuildProductKeys.GetValue("ios_um_sinakey");
if (!string.IsNullOrEmpty(sinakey))
{
dict2 = array.AddDict();
dict2.SetString("CFBundleURLName", "");
array2 = dict2.CreateArray("CFBundleURLSchemes");
array2.AddString("wb" + sinakey);
}
//[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"prefs:root=WIFI"]];
dict2 = array.AddDict();
dict2.SetString("CFBundleURLName", "");
array2 = dict2.CreateArray("CFBundleURLSchemes");
array2.AddString("prefs");
dict.SetString("CFBundleIdentifier", Application.bundleIdentifier);
var lines = File.ReadAllText("iOS产品白名单").Split('\n');
array = dict.CreateArray("LSApplicationQueriesSchemes");
foreach (var line in lines)
{
array.AddString(line.Replace("\r", "").Replace("\n", ""));
}
dict.SetString("View controller-based status bar appearance", "NO");
plistDocument.WriteToFile(path);
}
}
}
xcodebuild.sh
#!/bin/sh
xcodePath=$1
projectPath=$2
archivePath="build/Unity-iPhone.xcarchive"
exportPath=$3 #Unity-iPhone.ipa
provisioningProfile=$4 #iOS_Distribution
cd $projectPath
${xcodePath}/Contents/Developer/usr/bin/xcodebuild -scheme Unity-iPhone clean archive -archivePath $archivePath
${xcodePath}/Contents/Developer/usr/bin/xcodebuild -exportArchive -exportFormat ipa -archivePath $archivePath -exportPath $exportPath -exportProvisioningProfile "$provisioningProfile"
# ${xcodePath}/Contents/Developer/usr/bin/xcodebuild build
# cd ${projectPath}/build/Release-iphoneos/
# ${xcodePath}/Contents/Developer/usr/bin/xcrun -sdk iphoneos packageapplication -v *.app -o $exportPath
exit $?