Unity打企业包shell脚本

打iOS企业包真是神烦神慢,看了一堆文章,一开始打算用jenkins,但是鉴于配jenkins又需要新配一个mac用户……出于各种考虑pass掉了这个方法,但是未来为了效率应该还是会配个。
搜了半天没有什么非常适合的能直接用的脚本,要么参数太多,要么感觉不是在打企业包,于是还是花了一天写了一下。

主要参考:
雨松MOMO Unity3D研究院之IOS全自动打包生成ipa
Shell脚本——Xcode脚本打包

实现功能

一个命令即可获得Xcode工程(位置在项目目录下),自动修改xcode文件以及framework做到适配iphoneX,最后打出企业包(在Desktop)
当前版本比较傻逼的是:路径写的都是绝对路径……配置也都分别写死在了shell脚本和unity脚本里_(:з」∠)_
未来预期:传不同参得到企业/公司包

前期准备

Unity(……废话)
XCode(……继续废话)
企业开发者账号及一堆设置,还得知道账号的teamID
plist文件(以前生成的或者自己写一个)

主要流程

  1. unity编译出XCode项目
  2. 修改XCode项目OC代码
  3. XCode编译出xcarchive文件
  4. 拿着plist打ipa
  5. TODO:自动上传到fir

Part I: shell调unity命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/bin/sh

#UNITY程序的路径#
UNITY_PATH=/Applications/Unity2017.3.1f1/Unity201731.app/Contents/MacOS/Unity
#游戏程序路径#
PROJECT_PATH=/Users/maggie/TestBuild/client

DATE=`date +%Y_%m_%d_%H_%M`
XCODE_PATH="ios-"$DATE
echo "文件夹名:"$XCODE_PATH

#调用ShellBuildApp.BuildIOSEnterpirse 将unity导出成xcode工程#
$UNITY_PATH -projectPath $PROJECT_PATH -executeMethod ShellBuildApp.BuildIOSEnterpirse "${XCODE_PATH}" -quit

echo "XCODE工程生成完毕"

#调了项目里,本文件夹另一个脚本
./xcodeArchieve.sh "/Users/maggie/TestBuild/app_build/${DATE}"

echo "打包完毕"

Part II: unity打包脚本

这个步骤有很大的发挥空间……
公司的项目在这步里打了ab包,生成Xcode项目,再修改了xcode项目

ShellBuildApp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
public class ShellBuildApp :BuildAppBase {

//刚才shell调的就是这里
public static void BuildIOSEnterpirse() {
//ab包
//AssetBundleBuilder.BuildAssetBundle(BuildTarget.iOS, isQuickBuild());
BuildIOSEnterprise(GetBuildNameFromShell());
}

//从shell获得参数
private static string GetBuildNameFromShell() {

foreach(string arg in System.Environment.GetCommandLineArgs()) {
if(arg.StartsWith("ios")) {
return arg.Split("-"[0])[1];
}
}
return "NoName";
}

//unity打包设置
public static void BuildIOSEnterprise(string targetName) {
string targetDir = XT.CombinPath(FileLoaderUtil.GetRepositoryRootDir(), "/app_build");
BuildTargetGroup targetGroup = BuildTargetGroup.iOS;
BuildTarget buildTarget = BuildTarget.iOS;

if (Directory.Exists(targetDir)) {
if (Directory.Exists(targetName)) {
Directory.Delete(targetName);
}
}
else {
Directory.CreateDirectory(targetDir);
}

//这几行很关键 直接决定自动签名是否成功
PlayerSettings.iOS.iOSManualProvisioningProfileID = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx";
PlayerSettings.iOS.appleDeveloperTeamID = "XXXXXXXXXX";
PlayerSettings.applicationIdentifier = "com.xxxx.xxxx";
PlayerSettings.iOS.appleEnableAutomaticSigning = true;
PlayerSettings.SetScriptingBackend(targetGroup, ScriptingImplementation.IL2CPP);
PlayerSettings.bundleVersion = "v0.0.1";
PlayerSettings.SetScriptingDefineSymbolsForGroup(targetGroup, "");
PlayerSettings.productName = "xxxx";
AssetDatabase.Refresh();

EditorUserBuildSettings.SwitchActiveBuildTarget(targetGroup, buildTarget);

GenericBuild(SCENES, targetDir + "/" + targetName, buildTarget, BuildOptions.None);
}
}

BuildAppBase

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
using System;

public class BuildAppBase {
public static string[] SCENES = FindEnabledEditorScenes();

protected static string[] FindEnabledEditorScenes() {
List<string> EditorScenes = new List<string>();
foreach (EditorBuildSettingsScene scene in EditorBuildSettings.scenes) {
if (!scene.enabled) continue;
EditorScenes.Add(scene.path);
}
return EditorScenes.ToArray();
}

protected static void GenericBuild(string[] scenes, string targetDir, BuildTarget buildTarget, BuildOptions buildOptions) {

string errorMsg = BuildPipeline.BuildPlayer(scenes, targetDir, buildTarget, buildOptions);

if (errorMsg.Length > 0) {
throw new Exception("BuildPlayer failure: " + errorMsg);
}
}
}

后处理

参照:
Unity3D研究院之IOS全自动编辑framework、plist、oc代码 by 雨松
unity-iPhoneX适配 by wilker
Unity适配iPhoneX iOS 11+ Home键易误操作的问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
    [PostProcessBuild(100)]
public static void OnPostProcessBuild(BuildTarget target, string pathToBuiltProject) {
if (target != BuildTarget.iOS) {
//Debug.LogWarning("Target is not iPhone. XCodePostProcess will not run");
return;
}
string path = Path.GetFullPath(pathToBuiltProject);

XCProject project = new XCProject(pathToBuiltProject);
string[] files = Directory.GetFiles(Application.dataPath, "*.projmods", SearchOption.AllDirectories);
foreach(string file in files) {
LogMgr.LogDebug(file);
project.ApplyMod(file);
}

EditXAdaptCode(path);
EditXGestureCode(path);
project.Save();
}

public static void EditXAdaptCode (string path)
{
//XClass UnityAppController = new XClass (filePath + "/Classes/UnityAppController.mm");


string src = @" _window = [[UIWindow alloc] initWithFrame: [UIScreen mainScreen].bounds];";
string dst = @"// _window = [[UIWindow alloc] initWithFrame: [UIScreen mainScreen].bounds];

CGRect winSize = [UIScreen mainScreen].bounds;
if (winSize.size.width / winSize.size.height > 2) {
winSize.size.width -= 64;
winSize.origin.x = 32;
}
_window = [[UIWindow alloc] initWithFrame: winSize];

";
string unityAppControllerPath = path + "/Classes/UnityAppController.mm";
XClassExt UnityAppController = new XClassExt (unityAppControllerPath);
UnityAppController.Replace (src, dst);


}

public static void EditXGestureCode(string path) {
string src = @"- (UIRectEdge)preferredScreenEdgesDeferringSystemGestures
{
UIRectEdge res = UIRectEdgeNone;
if (UnityGetDeferSystemGesturesTopEdge())
res |= UIRectEdgeTop;
if (UnityGetDeferSystemGesturesBottomEdge())
res |= UIRectEdgeBottom;
if (UnityGetDeferSystemGesturesLeftEdge())
res |= UIRectEdgeLeft;
if (UnityGetDeferSystemGesturesRightEdge())
res |= UIRectEdgeRight;
return res;
}

- (BOOL)prefersHomeIndicatorAutoHidden
{
return UnityGetHideHomeButton();
}
";
string dest = @"//adjust finished
- (UIRectEdge)preferredScreenEdgesDeferringSystemGestures
{
return UIRectEdgeAll;
}
";

string unityAppControllerPath = path + "/Classes/UI/UnityViewControllerBase+iOS.mm";
XClassExt UnityAppController = new XClassExt (unityAppControllerPath);
UnityAppController.Replace (src, dest);

}

上面改代码用到的文件修改类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.IO;

namespace UnityEditor.XCodeEditor {
public partial class XClassExt : System.IDisposable {

private string filePath;

public XClassExt (string fPath)
{
filePath = fPath;
if (!System.IO.File.Exists (filePath)) {
Debug.LogError (filePath + "not found in path.");
return;
}
}

public void WriteBelow (string below, string text)
{
StreamReader streamReader = new StreamReader (filePath);
string text_all = streamReader.ReadToEnd ();
streamReader.Close ();

int beginIndex = text_all.IndexOf (below);
if (beginIndex == -1) {
Debug.LogError (filePath + " not found sign in " + below);
return;
}

int endIndex = text_all.LastIndexOf ("\n", beginIndex + below.Length);

text_all = text_all.Substring (0, endIndex) + "\n" + text + "\n" + text_all.Substring (endIndex);

StreamWriter streamWriter = new StreamWriter (filePath);
streamWriter.Write (text_all);
streamWriter.Close ();
}

public void Replace (string below, string newText)
{
StreamReader streamReader = new StreamReader (filePath);
string text_all = streamReader.ReadToEnd ();
streamReader.Close ();

int beginIndex = text_all.IndexOf (below);
if (beginIndex == -1) {
Debug.LogError (filePath + " not found sign in " + below);
return;
}

text_all = text_all.Replace (below, newText);
StreamWriter streamWriter = new StreamWriter (filePath);
streamWriter.Write (text_all);
streamWriter.Close ();

}

public void Dispose ()
{

}
}
}

Part III: xcode build

xcodeArchieve.sh

这是在Xcode工程生成后被调的脚本
已把plist文件放在了项目的ios_builder文件夹下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#!/bin/sh

Proj_path=$1
Date=`date +%Y_%m_%d_%H_%M`
echo "path:${Proj_path}"
SECONDS=0
#准备
Release_path="/Users/maggie/Desktop/Unity-iPhone"${Date}
mkdir -p "$Release_path"

Archive_path="${Release_path}/${Date}.xcarchive"

#开始
cd ${Proj_path}

#open ${Proj_path}

Configuration="Release"

Proj_name="Unity-iPhone"
Log_path=${Release_path}"/log"
Project_name="${Proj_path}/${Proj_name}.xcodeproj"
IPA_path=${Release_path}"/ipa"
ExportOptionsPlist="/Users/maggie/TestBuild/ios_builder/ExportOptionsEnterprise.plist"

#clean
xcodebuild clean -configuration ${Configuration} >> Log_path

#build
xcodebuild -configuration "${Configuration}" >> $Log_path
if [ $? = 0 ]; then
echo -e "\033[32m************* xcodebuild 编译 完成 **************\033[0m"
echo ''
else
#revertVersionNum;
echo -e "\033[31m************* xcodebuild 编译 失败 **************\033[0m"
echo ''
exit 1;
fi

#TODO 改版本号等

#archive
xcodebuild archive -project "${Project_name}" -scheme "${Proj_name}" -configuration "${Configuration}" -archivePath "${Archive_path}" >> $Log_path
if [ $? = 0 ]; then
echo -e "\033[32m************* xcodebuild 导xcarchive 完成 **************\033[0m"
echo ''
else
#revertVersionNum;
echo -e "\033[31m************* xcodebuild 导xcarchive 失败 **************\033[0m"
echo ''
exit 1;
fi

# IPA
xcodebuild -exportArchive -archivePath "${Archive_path}" -exportPath "${IPA_path}" -exportOptionsPlist "${ExportOptionsPlist}" >> $Log_path
if [ $? = 0 ]; then
echo -e "\033[32m************* 导出 ipa 包完成 **************\033[0m"
echo ''
else
#revertVersionNum;
echo -e "\033[31m************* 导出 ipa 包失败 **************\033[0m"
echo ''
exit 1;
fi
#输出总用时
echo -e "\033[32m************* 打包完成. 耗时: ${SECONDS}s **************\033[0m"
echo ''