系列传送门
IOS:
IOS:使用shell命令打包并上传Itunes
Unity3d:
Unity3d:Canvas适配屏幕分辨率与锚点(Anchors与Pivot)
Unity3d:在屏幕边缘显示其他玩家方位
Unity3d:命令行打包Android
Unity3d:命令行编译IOS
Unity3d:使用Jenkins自动编译打包IOS(只能打包Development)
Unity3d:使用Jenkins自动编译打包IOS(打包Ad-hoc,上传itunes)
安装并配置Jenkns
1. 下载完后,安装
2. 修改jenkins启动用户
参考:http://www.xuanyusong.com/archives/3349
安装完之后jenkins会自动启动,而且使用root用户启动的,这会在构建时造成一些麻烦,比如执行权限,文件访问权限等
所以需要把jenkins先关闭,然后修改jenkins的启动用户和当前登陆系统的用户一致
- 停止jenkins
sudo launchctl unload /Library/LaunchDaemons/org.jenkins-ci.plis
- 修改org.jenkins-ci.plis文件
- 修改GroupName和当前登陆系统的用户的Group Name一致
- 修改UserName和当前登陆系统的用户的User Name一致
- 启动jenkins
不推荐:launchctl load /Library/LaunchDaemons/org.jenkins-ci.plist
推荐: java -jar jenkins.war
3. 访问http://127.0.0.1:8008
打开网页后,会让输入安装密码,这个密码在:
/User/当前系统登陆用户/.jenkins/secrets/initialAdminPassword
然后下一步配置Jenkins网页的登陆密码,信息要全部填写完成后,会提示安装插件,直接选安装推荐的插件
4. 安装我们构建打包需要插件
安装时,记得勾选自动重启jenkins
- Unity3d Plugin
- Xcode Integration:该插件只能打development的ipa包,不能打ad-hoc和app-store的包,也不能上传iTunes
- Keychains and Provisioning Profiles Plugin
- Perforce Plugin(可选,如果Unity3d项目源代码用Perforce管理,可以使用该插件)
5. 设置插件配置
配置Xcode Builder
进入jenkins主页->左侧系统管理->系统设置->Xcode Builder
Xcode路径之类的,插件会自动补充,无需填写
- Development Team(可选,配置构建时可自己指定)
- Team Name:***********(可以在开发者后台查到)
- Team ID:***********(可以在开发者后台查到)
- keychains
- keychain Name:login.keychain(自己随便定义)
- keychain Path:/Users/当前系统登陆用户/Library/Keychains/login.keychain(路径中包括xxxx.keychain文件)
- keychain Password:********(当前系统登陆用户的密码)
配置Unity3d
进入jenkins主页->左侧系统管理->全局工具配置->Unity3d
- 别名:Unity3d(自己随便定义)
- 安装目录:/Applications/Unity/Unity.app(注意:该插件会自己在后面拼接“Contents/MacOS”或“Editor/Unity.exe”)
配置Keychains and Provisioning Profiles Management
进入jenkins主页->左侧系统管理->全局工具配置->Keychains and Provisioning Profiles Management
证书说明:
- ios_development.cer:需要从开发者后台下载(左侧certificate栏目里)
- xxxxxxx.mobileprovision(有development版(这里用开发版),ad-hoc的distribution版,正式distribution版):需要从开发者后台下载(左侧Provisioning Profiles栏目里)
- xxxxxxx.developerprofile:需要从Xcode中导出(Xcode->Preferences->Accounts(需要在这里先登陆开发者账号)->左下角齿轮按钮->Export Apple ID And Code Signing Assets)
- 上传login.keychain
- 配置Keychains
- Filename:login.keychain(会自动填写)
- Password:当前系统登陆用户的密码
- Description:(自己随便定义)
- Identities:
- Code Sign Identity:iPhone Developer:XXXXXX(钥匙串中,苹果开发者证书简介信息中的常用名称,打开系统钥匙串,把xxxxxxx.developerprofile复制进去即可)
- 配置Keychains
- 上传xxxxxxx.mobileprovision
- 配置Provisioning Profiles
- Provisioning Profiles Directory Path:/Users/当前系统登陆用户/Library/MobileDevice/Provisioning Profiiles
- Filename:(会自动填写)
- UUID:(会自动填写)
- 配置Provisioning Profiles
Jenkins配置结束
正式开工,创建构建
1. 创建构建
创建一个自由风格的软件项目
- 项目名:Unity3d_Build_IOS(自己随便定义)
2. 构建配置
修改workspace
默认workspace:
/Users/当前系统登陆用户/.jenkins/workspace/Unity3d_Build_IOS(项目名)
顶部选项卡->General->高级选项->选择“使用自定义的工作空间”
- 目录:自定义的工作空间路径
- 显示名称:Unity3dBuildXXX(如果填写了,会覆盖上面的项目名:Unity3d_Build_IOS)
配置Unity3d编译相关
顶部选项卡->构建->增加构建步骤->选择Invoke Unity3d Editor
- Unity3d installation name:Unity3d(会自动填写,也就是在全局工具配置配置时写的"Unity3d"别名)
- Editor conmmand line arguments:
-quit -batchmode -executeMethod PerformBuild.CommandLineBuild -projectPath 要编译的unity3d项目目录
插件参考:https://wiki.jenkins.io/display/JENKINS/Unity3dBuilder+Plugin
脚本参考:http://www.cnblogs.com/yinghuochong/archive/2013/09/01/3294940.html
命令参考:https://docs.unity3d.com/Manual/CommandLineArguments.html
项目脚本 PerformBuild.cs,写好后 ,放在Editor目录下
- 注意:脚本里写的unity3d的IOS编译输出目录一定要记得,后面会用得着
- 此处unity3d的IOS编译输出目录为:/Users/当前系统登陆用户/Documents/Project/build/iPhone
配置完成后,可以点击“立即构建”进行测试,看unity3d项目编译是否正常
点击左下角构建记录,进入该记录信息页,再点击左侧“Console Output”即可查看构建日志输出
注意:从日志里看到,编译时,有无权限删除IOS编译输出目录的问题,此时需要做以下配置:
顶部选项卡->构建->增加构建步骤->选择Execute shell
Commands输入:
rm -rf /Users/当前系统登陆用户/Documents/Project/build/iPhone(unity3d的IOS编译输出目录)
命令配置好后,把Execute shell拖到Invoke Unity3d Editor的前面,此时在Unity3d指定编译之前,会先执行配置的shell命令
配置IOS编译相关
上面配置好并测试完成后,继续配置Xcode编译相关
顶部选项卡->构建环境->勾选Keychains and Code signing Identities
勾选即可,保存后,刷新即可看到内容被自动填充(也就是之前配置的全局工具配置->Keychains and Provisioning Profiles Management)
顶部选项卡->构建环境->勾选Mobile Provisioning Profiles
勾选即可,保存后,刷新即可看到内容被自动填充(也就是之前配置的全局工具配置->Keychains and Provisioning Profiles Management)
顶部选项卡->构建->增加构建步骤->选择Xcode
配置General build settings
- Target:Unity-iPhone
- 如果不知道应该写,通过命令行工具,可以进入上一步生成的IOS项目目录(unity3d的IOS编译输出目录),执行“xcodebuild -list”即可看到
- Clear before build:Yes
- Configuration:release( 或者 debug,看需要)
- 勾选 Pack applicatin,build and sign ipa
- Export method:develoment(看需要,如果Mobile Provisioning Profiles选择的是develoment证书,则这里需要配置development,如果是ad-hoc的distribution证书,则这里需要配置ad-hoc,如果是正式上线的证书,则这里需要配置app-store)
- .ipa filename pattern:${BUILD_DATE}(生成的ipa文件名字)
- Output directory:/Users/当前系统登陆用户/Desktop(这里是放在当前系统用户的桌面,自己随便定义)
配置Code signing & OS X keychain options
- Development Team:*******(下拉选择这个,也就是之前配置的系统设置->Xcode Builder->Development Team->Team Name)
- Development Team ID:(当上面Development Team选择None时,此处需要手动输入TeamID,可以在开发者后台查到)
- 勾选Unlock keychain:
- Keychain:login.keychain(下拉选择这个,也就是之前配置的系统设置->Xcode Builder->keychains)
- Keychain path:(当上面Keychain选择None时,此处需要手动输入xxxx.keychain所在路径,路径中包括xxxx.keychain文件)
- Keychain password:(当上面Keychain选择None时,此处需要手动输入xxxx.keychain的解锁密码)
- Keychain:login.keychain(下拉选择这个,也就是之前配置的系统设置->Xcode Builder->keychains)
配置Advanced Xcode build options
-
Xcode Schem File:Unity-iPhone
- 如果不知道应该写,通过命令行工具,可以进入上一步生成的IOS项目目录(unity3d的IOS编译输出目录),执行“xcodebuild -list”即可看到
SDK:iOS 11.2 - 如果不知道应该写,通过命令行工具,可以进入上一步生成的IOS项目目录(unity3d的IOS编译输出目录),执行“xcodebuild -list”即可看到
-
Custom xcodebuild arguments:-ENABLE_BITCODE=NO -allowProvisioningUpdates
- 比如,编译时不需要开启 BITCODE,自此出填写:-ENABLE_BITCODE=NO,这个配置如果在Unity中配置过了,此处可以写
- 比如,编译时需要动态更新证书,自此出填写:-allowProvisioningUpdates
Xcode Project Directory:/Users/当前系统登陆用户/Documents/Project/build/iPhone(unity3d的IOS编译输出目录)
创建构建并配置结束
注意事项#
- 如果出现“ld ... normal arm64”类的错误,就倒着分析log,可能有以下异常引起:
- 看有没有什么framework not found的异常
- 打包发布版本的时候,会出现找不到对应的证书:
- 要钥匙串中有开发证书
附:在Unity3d中动态添加IOS需要的Framework和plist参数方法
#if UNITY_IOS
using UnityEditor;
using UnityEditor.iOS.Xcode;
using System.Collections.Generic;
using System.IO;
#endif
public static class PBXHelper
{
public const string PROJECT_ROOT = "$(PROJECT_DIR)/";
public const string IMAGE_XCASSETS_DIRECTORY_NAME = "Unity-iPhone";
public const string LINKER_FLAG_KEY = "OTHER_LDFLAGS";
public const string FRAMEWORK_SEARCH_PATHS_KEY = "FRAMEWORK_SEARCH_PATHS";
public const string LIBRARY_SEARCH_PATHS_KEY = "LIBRARY_SEARCH_PATHS";
public const string ENABLE_BITCODE_KEY = "ENABLE_BITCODE";
public const string DEVELOPMENT_TEAM = "DEVELOPMENT_TEAM";
public const string GCC_ENABLE_CPP_EXCEPTIONS = "GCC_ENABLE_CPP_EXCEPTIONS";
public const string GCC_ENABLE_CPP_RTTI = "GCC_ENABLE_CPP_RTTI";
public const string GCC_ENABLE_OBJC_EXCEPTIONS = "GCC_ENABLE_OBJC_EXCEPTIONS";
public const string INFO_PLIST_NAME = "Info.plist";
public const string URL_TYPES_KEY = "CFBundleURLTypes";
public const string URL_TYPE_ROLE_KEY = "CFBundleTypeRole";
public const string URL_IDENTIFIER_KEY = "CFBundleURLName";
public const string URL_SCHEMES_KEY = "CFBundleURLSchemes";
public const string APPLICATION_QUERIES_SCHEMES_KEY = "LSApplicationQueriesSchemes";
#if UNITY_IOS
/// <summary>
/// </summary>
/// <param name="buildTarget">Build target.</param>
/// <param name="buildPath">Build path.</param>
[PostProcessBuild]
public static void OnPostprocessBuild (BuildTarget buildTarget, string buildPath)
{
if (buildTarget != BuildTarget.iOS)
return;
string pbxProjPath = PBXProject.GetPBXProjectPath (buildPath);
pbxProject = new PBXProject ();
pbxProject.ReadFromString (File.ReadAllText (pbxProjPath));
string targetGuid = pbxProject.TargetGuidByName (PBXProject.GetUnityTargetName ());
// 1、设置关闭Bitcode
pbxProject.SetBuildProperty (targetGuid, ENABLE_BITCODE_KEY, "NO");
// 2、添加Framework
pbxProject.AddFrameworkToProject (targetGuid, "系统自带的.framework", false);
pbxProject.AddFrameworkToProject (targetGuid, "系统自带的.framework", false);
pbxProject.AddFrameworkToProject (targetGuid, "系统自带的.framework", false);
// 3、添加tbd
pbxProject.AddFileToBuild(targetGuid, pbxProject.AddFile("usr/lib/" + "系统自带的.tbd", "Frameworks/" + "系统自带的.tbd", PBXSourceTree.Sdk));
// 4、添加Privaty
SetInfoPlist (buildPath);
File.WriteAllText (pbxProjPath, pbxProject.WriteToString ());
UnityEngine.Debug.Log ("PBXProject : ---->" + pbxProject.WriteToString ());
}
public static void SetInfoPlist (string buildPath)
{
List<string> privacySensiticeData = new List<string> ();
// 添加权限
privacySensiticeData.Add ("NSMicrophoneUsageDescription");
privacySensiticeData.Add ("NSCameraUsageDescription");
privacySensiticeData.Add ("NSLocationAlwaysAndWhenInUseUsageDescription");
PlistDocument plist = GetInfoPlist (buildPath);
SetPrivacySensiticeData (plist, privacySensiticeData, "Privacy");
plist.WriteToFile (GetInfoPlistPath (buildPath));
UnityEngine.Debug.Log ("PLIST : ---->" + plist.WriteToString ());
}
private static void SetPrivacySensiticeData (PlistDocument plist, List<string> permission, string description = "")
{
PlistElementDict rootDict = plist.root;
int count = permission.Count;
for (int i = 0; i < count; i++)
{
rootDict.SetString (permission[i], description);
}
}
private static string GetInfoPlistPath (string buildPath)
{
return Path.Combine (buildPath, INFO_PLIST_NAME);
}
private static PlistDocument GetInfoPlist (string buildPath)
{
string plistPath = GetInfoPlistPath (buildPath);
PlistDocument plist = new PlistDocument ();
plist.ReadFromFile (plistPath);
return plist;
}
#endif
}