Python打包脚本参考

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;
    }
}



首页 我的博客
粤ICP备17103704号