build.py
import json
import os
import tail
import subprocess
import sys
import _thread
import time
import re
import send2trash
import xcode_pre_build
AppName = "Cascading_Stars"
xcode_build_type = "Release"
def tail_log(log_file):
print("into tail file = %s" % log_file)
while True:
if os.path.exists(log_file):
print("begin tail file = %s" % log_file)
break
t = tail.tail(log_file, print_log)
def print_log(txt):
print(txt)
def build_android_app(ide_path, log_path, project_path, params):
time1 = time.time()
if os.path.exists(log_path):
os.remove(log_path)
_thread.start_new_thread(tail_log, (log_path, ))
if params['aot'] == "1":
cmd = ide_path + " -quit" + " -batchmode" + " -logfile " + log_path + " -projectPath " + project_path + " -executeMethod MenuItemTools.HybridClrAotBuildAndroid"
process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=project_path)
process.wait()
cmd = ide_path + " -quit" + " -batchmode" + " -logfile " + log_path + " -projectPath " + project_path + " -executeMethod MenuItemTools.PackageApk"
process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=project_path)
process.wait()
stdout,stderr = process.communicate()
print(stdout.decode())
if stderr:
print(stderr.decode())
time2 = time.time()
print("build android time = " + str(time2-time1))
def export_xcode(ide_path, log_path, project_path):
print("export unity to xcode")
if os.path.exists(log_path):
os.remove(log_path)
cmd = ide_path + " -quit" + " -batchmode" + " -logfile " + log_path + " -projectPath " + project_path + " -executeMethod MenuItemTools.PackageIOS"
_thread.start_new_thread(tail_log, (log_path, ))
process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=project_path)
process.wait()
stdout,stderr = process.communicate()
print(stdout.decode())
if stderr:
print(stderr.decode())
def xcode_to_app(xcode_path, params):
name = None
if str(params['debug']) == "0":
name = "Release"
else:
name = "Debug"
file_name = AppName + "-" + name + "-" + params['appver']
export_path = os.path.join(xcode_path, "..", "..", "exportipa")
archive_path = xcode_path + "/archive/export.xcarchive"
process = subprocess.Popen('cd %s;xcodebuild clean -configuration %s' % (xcode_path, xcode_build_type), shell=True)
process.wait()
#process = subprocess.Popen('cd %s;pod update' % (xcode_path), shell=True)
#process.wait()
process = subprocess.Popen(
'cd %s;xcodebuild archive -workspace Unity-iPhone.xcworkspace -scheme Unity-iPhone -configuration %s -archivePath %s -destination "generic/platform=iOS" || exit' % (
xcode_path, xcode_build_type, archive_path), shell=True)
process.wait()
process = subprocess.Popen('mkdir %s' % export_path, shell=True)
process.wait()
plist_path = os.path.join(py_path, "exportOptions.plist")
process = subprocess.Popen('xcodebuild -exportArchive -archivePath %s -exportOptionsPlist %s -exportPath %s/%s' % (
archive_path, plist_path, export_path, file_name), shell=True)
process.wait()
def build_ios_app(ide_path, log_path, project_path, xcode_path, params):
time1 = time.time()
if params['aot'] == "1":
cmd = ide_path + " -quit" + " -batchmode" + " -logfile " + log_path + " -projectPath " + project_path + " -executeMethod MenuItemTools.HybridClrAotBuildIOS"
process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=project_path)
process.wait()
export_xcode(ide_path, log_path, project_path)
time.sleep(2)
#i2cppbuild_path = os.path.join(project_path, "HybridCLRData/iOSBuild")
#process = subprocess.Popen('cd %s;bash ./build_libil2cpp.sh' %(i2cppbuild_path) , shell=True)
#process.wait()
xcode_pre_build.pre_build(xcode_path, project_path)
xcode_to_app(xcode_path, params)
time2 = time.time()
print("build ios time = " + str(time2-time1))
def clear_server_data(project_path, params):
data_path = None
if params['ios'] == "1":
data_path = os.path.join(project_path, "ServerData", "iOS")
else:
data_path = os.path.join(project_path, "ServerData", "Android")
if os.path.exists(data_path):
send2trash.send2trash(data_path)
def change_version_code(project_path, params):
appver = '"AppVer" : "' + params["appver"] + '"'
resver = '"ResVer" : "' + params["resver"] + '"'
version_path = os.path.join(project_path, "Assets", "Resources", "Version", "Version.bytes")
stream = open(version_path, 'r')
content = stream.read()
stream.close()
pattern = re.compile(r'"AppVer" : "([\d\.]*)"')
content = re.sub(pattern, appver, content)
pattern = re.compile(r'"ResVer" : "([\d\.]*)"')
content = re.sub(pattern, resver, content)
stream = open(version_path, 'w')
stream.write(content)
stream.close()
def change_debug_code(project_path, params):
debug = None
channel = None
if str(params['debug']) == "0":
debug = '''Debug = false'''
channel = '''{
"Head" : "Official",
"Version" : "http://update.whales-entertainment.com:9850/Update/Official/",
"Res" : "http://update.whales-entertainment.com:9850/Update/Official/",
"NeedAccount": false
}'''
else:
debug = '''Debug = true'''
channel = '''{
"Head": "Test",
"Version": "http://88lsj.com:9850/Update/Test/",
"Res": "http://88lsj.com:9850/Update/Test/",
"NeedAccount": false
}'''
config_path = os.path.join(project_path, "Assets", "Frame", "Runtime", "Config", "RK_FrameConfig.cs")
stream = open(config_path, 'r')
content = stream.read()
stream.close()
content = content.replace('''Debug = false''', debug)
content = content.replace('''Debug = true''', debug)
stream = open(config_path, 'w')
stream.write(content)
stream.close()
config_path = os.path.join(project_path, "Assets", "StreamingAssets", "ChannelInfo.bytes")
#stream = open(config_path, 'r')
#content = stream.read()
#stream.close()
#content = content.replace('''Official''', name)
#content = content.replace('''Test''', name)
stream = open(config_path, 'w')
stream.write(channel)
stream.close()
def delete_striaotdlltemp(project_path):
#这鬼东西 删不掉一直报错
aotdll_temp_path = os.path.join(project_path, "HybridCLRData/StrippedAOTDllsTempProj")
if os.path.exists(aotdll_temp_path):
send2trash.send2trash(aotdll_temp_path)
def set_proxy():
process = subprocess.Popen('export.UTF-8', shell=True)
process.wait()
process = subprocess.Popen('export LANGUAGE=en_US.UTF-8', shell=True)
process.wait()
process = subprocess.Popen('export LC_ALL=en_US.UTF-8', shell=True)
process.wait()
process = subprocess.Popen('export http_proxy=http://127.0.0.1:7890', shell=True)
process.wait()
process = subprocess.Popen('export https_proxy=http://127.0.0.1:7890', shell=True)
process.wait()
process = subprocess.Popen('git config --global http.proxy http://127.0.0.1:7890', shell=True)
process.wait()
process = subprocess.Popen('git config --global http.proxy https://127.0.0.1:7890', shell=True)
process.wait()
def unset_proxy():
process = subprocess.Popen('git config --global --unset http.proxy', shell=True)
process.wait()
process = subprocess.Popen('git config --global --unset https.proxy', shell=True)
process.wait()
if __name__ == "__main__":
default_params = '''{"ios":"1", "aot":"0" ,"appver":"1.0.0", "resver":"1000", "debug":"1"}'''
params = json.loads(default_params)
for arg in sys.argv:
par = arg.split("=")
if(len(par)>1):
params[par[0]] = par[1]
ide_path = "/Applications/Unity/Hub/Editor/2021.3.44f1c1/Unity.app/Contents/MacOS/Unity"
py_path = os.path.split(os.path.realpath(__file__))[0]
project_path = os.path.join(py_path, "../")
log_path = os.path.join(py_path, "package.log")
delete_striaotdlltemp(project_path)
clear_server_data(project_path, params)
change_version_code(project_path, params)
change_debug_code(project_path, params)
name = None
if str(params['debug']) == "0":
name = "Release"
else:
name = "Debug"
#set_proxy()
if str(params['ios']) == "0":
build_android_app(ide_path, log_path, project_path,params)
else:
xcode_path = os.path.join(project_path, 'Build', 'iPhone', AppName + "-" + params['appver'] + "-" + name)
build_ios_app(ide_path, log_path, project_path, xcode_path, params)
#unset_proxy()xcode_pre_build.py
import os
import shutil
import subprocess
def copy_il2cpplib(xcode_path, project_path):
origin_file = os.path.join(project_path, "HybridCLRData", "iOSBuild", "build", "libil2cpp.a")
target_file = os.path.join(xcode_path, "Libraries", "libil2cpp.a")
shutil.copy(origin_file, target_file)
print("copy src:" + origin_file + " to:" + target_file)
def change_plist(xcode_path):
info_plist_path = os.path.join(xcode_path, 'Info.plist')
stream = open(info_plist_path, 'r')
plist = stream.read()
stream.close()
plist = plist.replace('<string>fb-messenger-api</string>', '<string>fb</string><string>fb-messenger-share-api</string><string>fb-messenger-api</string>', 1)
stream = open(info_plist_path, 'w')
stream.write(plist)
stream.close()
deleteInfoPlist(info_plist_path,'NSAppTransportSecurity:NSAllowsArbitraryLoadsInWebContent')
def pre_build(xcode_path, project_path):
#copy_il2cpplib(xcode_path, project_path)
change_plist(xcode_path)
@staticmethod
def deleteInfoPlist(infoPlistPath,key):
(stauts, output) = subprocess.getstatusoutput(
'/usr/libexec/PlistBuddy -c \"Delete :{}\" {} '.format(key, infoPlistPath))
if stauts != 0:
if output.find('Does Not Exist') == -1:
print('删除Plist失败 key=%s,[%s]' % (key, infoPlistPath))-executeMethod MenuItemTools.PackageApk -executeMethod MenuItemTools.PackageIOS Unity中提供这两个静态方法打包
using UnityEditor;
using UnityEngine;
public class MenuItemTools
{
[MenuItem("Package/Build/Android", false,1)]
public static void PackageApk()
{
if (EditorUserBuildSettings.activeBuildTarget == BuildTarget.Android)
{
Package.PackageApk();
}
else
{
EditorUserBuildSettings.activeBuildTargetChanged = delegate ()
{
if (EditorUserBuildSettings.activeBuildTarget == BuildTarget.Android)
{
Package.PackageApk();
}
};
EditorUserBuildSettings.SwitchActiveBuildTarget(BuildTargetGroup.Android, BuildTarget.Android);
}
}
[MenuItem("Package/Build/Android-AAB", false, 1)]
public static void PackageAAB()
{
if (EditorUserBuildSettings.activeBuildTarget == BuildTarget.Android)
{
Package.PackageAAB();
}
else
{
EditorUserBuildSettings.activeBuildTargetChanged = delegate ()
{
if (EditorUserBuildSettings.activeBuildTarget == BuildTarget.Android)
{
Package.PackageAAB();
}
};
EditorUserBuildSettings.SwitchActiveBuildTarget(BuildTargetGroup.Android, BuildTarget.Android);
}
}
[MenuItem("Package/Build/Export to Xcode", false, 3)]
public static void PackageIOS()
{
if (EditorUserBuildSettings.activeBuildTarget == BuildTarget.iOS)
{
Package.PackageIos();
}
else
{
EditorUserBuildSettings.activeBuildTargetChanged = delegate ()
{
if (EditorUserBuildSettings.activeBuildTarget == BuildTarget.iOS)
{
Package.PackageIos();
}
};
EditorUserBuildSettings.SwitchActiveBuildTarget(BuildTargetGroup.iOS, BuildTarget.iOS);
}
}
}Package.cs
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using HybridCLR.Editor.Commands;
using UnityEngine;
using UnityEditor.Build.Content;
#if RK_GoogleAds
using GoogleMobileAds.Editor;
#endif
public class Package
{
public static void BuildSceneStream()
{
UnityEngine.Object[] scenes = Selection.GetFiltered(typeof(SceneAsset), SelectionMode.Assets);
foreach (UnityEngine.Object o in scenes)
{
string path = AssetDatabase.GetAssetPath(o);
if (!path.Contains(".unity")) continue;
string sceneName = o.name + ".scene";
BuildPipeline.BuildPlayer(new string[] { path }, "Res/Scenes/" + sceneName, BuildTarget.Android, BuildOptions.BuildAdditionalStreamedScenes);
}
}
public static void PackageApk()
{
PreparePackage();
EditorUserBuildSettings.buildAppBundle = false;
BuildPipeline.BuildPlayer(GetBuildScenes(), $"Build/Android/{GetAppFileName()}.apk", BuildTarget.Android, BuildOptions.CompressWithLz4HC);
}
public static void PackageAAB()
{
PreparePackage();
EditorUserBuildSettings.buildAppBundle = true;
BuildPipeline.BuildPlayer(GetBuildScenes(), $"Build/Android/{GetAppFileName()}.aab", BuildTarget.Android, BuildOptions.CompressWithLz4HC);
}
public static void PackageStandaloneWindows64()
{
PreparePackage();
BuildPipeline.BuildPlayer(GetBuildScenes(), $"Build/StandaloneWindows64/{AotConfig.ProductName}-{GetAppVer()}/{AotConfig.ProductName}.exe", BuildTarget.StandaloneWindows64, BuildOptions.CompressWithLz4HC);
}
public static void PackageIos()
{
PreparePackage();
string module = null;
if (RK_FrameConfig.IsDebug)
{
module = "Debug";
}
else
{
module = "Release";
}
BuildPipeline.BuildPlayer(GetBuildScenes(), $"Build/iPhone/{AotConfig.ProductName}-{GetAppVer()}-{module}", BuildTarget.iOS, BuildOptions.CompressWithLz4HC);
}
public static void PackageOSX()
{
PreparePackage();
BuildPipeline.BuildPlayer(GetBuildScenes(), $"Build/Osx/{AotConfig.ProductName}-{GetAppVer()}/{AotConfig.ProductName}", BuildTarget.StandaloneOSX, BuildOptions.CompressWithLz4HC);
}
private static string GetAppFileName()
{
string name = AotConfig.ProductName;
#if RK_GameDebug
name += "-Debug";
#else
name += "-Release";
#endif
#if RK_CSJ
name += "-CSJ";
#endif
#if RK_TapTap
name += "-TapTap";
#endif
#if RK_GoogleAds
name += "-Admod";
#endif
#if RK_TopOnAds
name += "-Topon";
#endif
if (AotConfig.IsSkipAd)
{
name += "-SkipAd";
}
else
{
if (AotConfig.IsTestAd)
{
name += "-TestAd";
}
}
if (AotConfig.NoPay)
{
name += "-NoPay";
}
name += "-" + AotConfig.DefaultLanguage;
name += "-" + GetAppVer();
return name;
}
public static void PreparePackage()
{
Debug.Log("PreparePackage");
//ChangeDebugSymbol();
//ChangeReviewSymbol();
//PlayerSettings.bundleVersion = GetAppVer();
//PlayerSettings.Android.bundleVersionCode = GetIntAppVer();
if (AotConfig.DefaultLanguage == "Chinese")
{
PlayerSettings.productName = AotConfig.SettingProductName_CN;
}
else
{
PlayerSettings.productName = AotConfig.SettingProductName_EN;
}
PlayerSettings.iOS.buildNumber = "0";
if(!AotConfig.DisableHybridCLR)
PrebuildCommand.GenerateAll();
PackageRes();
AssetDatabase.Refresh();
}
public static void ChangeDebugSymbol()
{
SymbolMenuItemTools.ChangeDebugSymbol(RK_FrameConfig.IsDebug);
}
public static void PackageRes()
{
ClearServerData();
//#if UNITY_EDITOR_WIN
// ProtobufTool.AllProto2CS();
//#endif
AotHotUpdateDll.BuildAndCopyABAOTHotUpdateDlls();
AASUtility.CleanPlayerContent();
PackageAtlas.PackAtlas();
AssetDatabase.Refresh();
AddressableTool.MarkAssets();
AddressableTool.BuildPlayerContent();
}
public static void HybridClrAotBuild()
{
string path = Path.Combine(Application.dataPath, "../Build/AotTemp");
if (Directory.Exists(path))
{
Directory.Delete(path, true);
}
BuildTarget target = EditorUserBuildSettings.activeBuildTarget;
CompileDllCommand.CompileDll(target);
Il2CppDefGeneratorCommand.GenerateIl2CppDef();
LinkGeneratorCommand.GenerateLinkXml(target);
BuildPipeline.BuildPlayer(GetBuildScenes(), $"Build/AotTemp/{AotConfig.ProductName}-{GetAppVer()}", target, BuildOptions.CompressWithLz4HC);
}
public static void ClearServerData()
{
string path = Path.Combine(Application.dataPath, "../","ServerData");
if (EditorUserBuildSettings.activeBuildTarget == BuildTarget.StandaloneWindows)
{
path = Path.Combine(path, "StandaloneWindows");
}
else if (EditorUserBuildSettings.activeBuildTarget == BuildTarget.StandaloneWindows64)
{
path = Path.Combine(path, "StandaloneWindows64");
}
else if (EditorUserBuildSettings.activeBuildTarget == BuildTarget.Android)
{
path = Path.Combine(path, "Android");
}
else if (EditorUserBuildSettings.activeBuildTarget == BuildTarget.iOS)
{
path = Path.Combine(path, "iOS");
}
Debug.Log(path);
if (Directory.Exists(path))
{
Directory.Delete(path, true);
}
}
private static string[] GetBuildScenes()
{
List<EditorBuildSettingsScene> editorBuildSettingsScenes = new List<EditorBuildSettingsScene>();
foreach (EditorBuildSettingsScene e in EditorBuildSettings.scenes)
{
string scenePath = e.path;
if (!string.IsNullOrEmpty(scenePath))
editorBuildSettingsScenes.Add(new EditorBuildSettingsScene(scenePath, true));
}
EditorBuildSettings.scenes = editorBuildSettingsScenes.ToArray();
List<string> names = new List<string>();
foreach (EditorBuildSettingsScene e in EditorBuildSettings.scenes)
{
if (e != null && e.enabled)
{
names.Add(e.path);
}
}
return names.ToArray();
}
private static string GetAppVer()
{
var json = Resources.Load<TextAsset>(BootMainConfig.LocalVersionPath).text;
var local_version = JsonUtility.FromJson<VersionInfo>(json);
return local_version.AppVer;
}
}