【Unity实战】Jenkins打包机联调脚本

这篇具有很好参考价值的文章主要介绍了【Unity实战】Jenkins打包机联调脚本。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

本文章主要围绕本人在Github上的一个开源脚本:

GitHub - Shepherd0619/JenkinsBuildUnity: A little script that connect Unity (with HybridCLR hot update) and Jenkins together.

这个脚本是一个用于在Jenkins中构建Unity项目的辅助工具。它的主要功能是构建HybridCLR热更新,并将生成的DLL文件和AOT元数据DLL文件复制到指定的目录,并将它们添加到Unity的Addressable Assets系统中。

如果您还不知道Jenkins和HybridCLR的话,建议先阅读一下往期博客和相关官方文档。

【Unity实战】HybridCLR热更快速集成-CSDN博客

Unity与Jenkins打包机实战-CSDN博客

脚本讲解

首先,我们来看一下这个脚本的结构。它是一个继承自MonoBehaviour的类,并且包含了一些静态方法用于构建热更新(也必须得是静态,否则的话后续Jenkins通过命令行调起Unity会比较麻烦,找不到这个函数)。

// JenkinsBuild
// Shepherd Zhu
// Jenkins Build Helper
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using HybridCLR.Editor;
using HybridCLR.Editor.Commands;
using HybridCLR.Editor.Settings;
using UnityEditor;
using UnityEditor.AddressableAssets;
using UnityEditor.AddressableAssets.Build;
using UnityEditor.AddressableAssets.Settings;
using UnityEngine;

public class JenkinsBuild : MonoBehaviour
{
    // 重要提醒:建议先在工作电脑上配好Groups和Labels,本脚本虽说遇到新文件可以添加到Addressables,但是不太可靠。

    [MenuItem("Shepherd0619/Build Hot Update")]
    /// <summary>
    /// 开始执行HybridCLR热更打包,默认打当前平台
    /// </summary>
    public static void BuildHotUpdate()
    {
        BuildHotUpdate(EditorUserBuildSettings.activeBuildTarget);
    }

    /// <summary>
    /// 开始执行HybridCLR热更打包
    /// </summary>
    /// <param name="target">目标平台</param>
    public static void BuildHotUpdate(BuildTarget target)
    {
        
    }

    public static void BuildHotUpdateForWindows64()
    {
        BuildHotUpdate(BuildTarget.StandaloneWindows64);
    }

    public static void BuildHotUpdateForiOS()
    {
        BuildHotUpdate(BuildTarget.iOS);
    }

    public static void BuildHotUpdateForLinux64()
    {
        BuildHotUpdate(BuildTarget.StandaloneLinux64);
    }

    public static void BuildHotUpdateForAndroid()
    {
        BuildHotUpdate(BuildTarget.Android);
    }

    /// <summary>
    /// 将热更DLL加入到Addressables
    /// </summary>
    /// <param name="dllPath">DLL完整路径</param>
    private static void SetHotUpdateDllLabel(string dllPath)
    {
        
    }

    /// <summary>
    /// 将AOT元数据DLL加入到Addressables
    /// </summary>
    /// <param name="dllPath">DLL完整路径</param>
    private static void SetAOTMetadataDllLabel(string dllPath)
    {
        
    }

    private static bool buildAddressableContent()
    {
        
    }
}

整个脚本的核心是`BuildHotUpdate`方法。这个方法接受一个`BuildTarget`参数,用于指定构建的目标平台。在方法中,首先打印出正在构建的目标平台,然后依次执行一系列构建热更新所需的命令。

/// <summary>
    /// 开始执行HybridCLR热更打包
    /// </summary>
    /// <param name="target">目标平台</param>
    public static void BuildHotUpdate(BuildTarget target)
    {
        Console.WriteLine(
            $"[JenkinsBuild] Start building hot update for {Enum.GetName(typeof(BuildTarget), target)}"
        );
        try
        {
            CompileDllCommand.CompileDll(target);
            Il2CppDefGeneratorCommand.GenerateIl2CppDef();

            // 这几个生成依赖HotUpdateDlls
            LinkGeneratorCommand.GenerateLinkXml(target);

            // 生成裁剪后的aot dll
            StripAOTDllCommand.GenerateStripedAOTDlls(target);

            // 桥接函数生成依赖于AOT dll,必须保证已经build过,生成AOT dll
            MethodBridgeGeneratorCommand.GenerateMethodBridge(target);
            ReversePInvokeWrapperGeneratorCommand.GenerateReversePInvokeWrapper(target);
            AOTReferenceGeneratorCommand.GenerateAOTGenericReference(target);
        }
        catch (Exception e)
        {
            Console.WriteLine(
                $"[JenkinsBuild] ERROR while building hot update! Message:\n{e.ToString()}"
            );
            return;
        }

        // 复制打出来的DLL并进行替换
        string sourcePath = Path.Combine(
            Application.dataPath,
            $"../{SettingsUtil.GetHotUpdateDllsOutputDirByTarget(target)}"
        );
        string destinationPath = Path.Combine(Application.dataPath, "HotUpdateDLLs");

        if (!Directory.Exists(sourcePath))
        {
            Console.WriteLine(
                "[JenkinsBuild] Source directory does not exist! Possibly HybridCLR build failed!"
            );
            return;
        }

        if (!Directory.Exists(destinationPath))
        {
            Console.WriteLine(
                "[JenkinsBuild] Destination directory does not exist! Abort the build!"
            );
            return;
        }

        // string[] dllFiles = Directory.GetFiles(sourcePath, "*.dll");
        
        // foreach (string dllFile in dllFiles)
        // {
        //     string fileName = Path.GetFileName(dllFile);
        //     string destinationFile = Path.Combine(destinationPath, fileName + ".bytes");
        //     Console.WriteLine($"[JenkinsBuild] Copy: {dllFile}");
        //     File.Copy(dllFile, destinationFile, true);
        // }

        List<string> hotUpdateAssemblyNames = SettingsUtil.HotUpdateAssemblyNamesExcludePreserved;
        for (int i = 0; i < hotUpdateAssemblyNames.Count; i++)
        {
            Console.WriteLine($"[JenkinsBuild] Copy: {hotUpdateAssemblyNames[i] + ".dll"}");
            File.Copy(sourcePath + "/" + hotUpdateAssemblyNames[i] + ".dll", Path.Combine(destinationPath, hotUpdateAssemblyNames[i] + ".dll.bytes"), true);
        }

        Console.WriteLine("[JenkinsBuild] Hot Update DLLs copied successfully!");

        // 复制打出来的AOT元数据DLL并进行替换
        Console.WriteLine("[JenkinsBuild] Start copying AOT Metadata DLLs!");
        sourcePath = Path.Combine(
            Application.dataPath,
            $"../{SettingsUtil.GetAssembliesPostIl2CppStripDir(target)}"
        );
        destinationPath = Path.Combine(Application.dataPath, "HotUpdateDLLs/AOTMetadata");

        if (!Directory.Exists(sourcePath))
        {
            Console.WriteLine(
                "[JenkinsBuild] Source directory does not exist! Possibly HybridCLR build failed!"
            );
            return;
        }

        if (!Directory.Exists(destinationPath))
        {
            Console.WriteLine(
                "[JenkinsBuild] Destination directory does not exist! Abort the build!"
            );
            return;
        }

        // 获取AOTGenericReferences.cs文件的路径
        string aotReferencesFilePath = Path.Combine(
            Application.dataPath,
            SettingsUtil.HybridCLRSettings.outputAOTGenericReferenceFile
        );

        if (!File.Exists(aotReferencesFilePath))
        {
            Console.WriteLine(
                "[JenkinsBuild] AOTGenericReferences.cs file does not exist! Abort the build!"
            );
            return;
        }

        // 读取AOTGenericReferences.cs文件内容
        string[] aotReferencesFileContent = File.ReadAllLines(aotReferencesFilePath);

        // 查找PatchedAOTAssemblyList列表
        List<string> patchedAOTAssemblyList = new List<string>();

        for (int i = 0; i < aotReferencesFileContent.Length; i++)
        {
            if (aotReferencesFileContent[i].Contains("PatchedAOTAssemblyList"))
            {
                while (!aotReferencesFileContent[i].Contains("};"))
                {
                    if (aotReferencesFileContent[i].Contains("\""))
                    {
                        int startIndex = aotReferencesFileContent[i].IndexOf("\"") + 1;
                        int endIndex = aotReferencesFileContent[i].LastIndexOf("\"");
                        string dllName = aotReferencesFileContent[i].Substring(
                            startIndex,
                            endIndex - startIndex
                        );
                        patchedAOTAssemblyList.Add(dllName);
                    }
                    i++;
                }
                break;
            }
        }

        // 复制DLL文件到目标文件夹,并添加后缀名".bytes"
        foreach (string dllName in patchedAOTAssemblyList)
        {
            string sourceFile = Path.Combine(sourcePath, dllName);
            string destinationFile = Path.Combine(
                destinationPath,
                Path.GetFileName(dllName) + ".bytes"
            );

            if (File.Exists(sourceFile))
            {
                Console.WriteLine($"[JenkinsBuild] Copy: {sourceFile}");
                File.Copy(sourceFile, destinationFile, true);
                //SetAOTMetadataDllLabel("Assets/HotUpdateDLLs/" + Path.GetFileName(dllName) + ".bytes");
            }
            else
            {
                Console.WriteLine("[JenkinsBuild] AOTMetadata DLL file not found: " + dllName);
            }
        }

        AssetDatabase.SaveAssets();

        Console.WriteLine("[JenkinsBuild] BuildHotUpdate complete!");

        AssetDatabase.Refresh();

		// 刷新后开始给DLL加标签
		//SetHotUpdateDllLabel("Assets/HotUpdateDLLs/Assembly-CSharp.dll.bytes");
        for (int i = 0; i < hotUpdateAssemblyNames.Count; i++)
        {
            SetHotUpdateDllLabel("Assets/HotUpdateDLLs/" + hotUpdateAssemblyNames[i] + ".dll.bytes");
        }

		foreach(string dllName in patchedAOTAssemblyList)
		{
			SetAOTMetadataDllLabel("Assets/HotUpdateDLLs/AOTMetadata/" + Path.GetFileName(dllName) + ".bytes");
		}

		Console.WriteLine("[JenkinsBuild] Start building Addressables!");
		buildAddressableContent();
    }

在`BuildHotUpdate`方法中,首先调用`CompileDllCommand.CompileDll`方法来编译DLL文件。然后调用`Il2CppDefGeneratorCommand.GenerateIl2CppDef`方法来生成Il2Cpp的定义文件。接下来,调用`LinkGeneratorCommand.GenerateLinkXml`方法来生成链接文件。然后,调用`StripAOTDllCommand.GenerateStripedAOTDlls`方法来生成裁剪后的AOT DLL文件。接着,调用`MethodBridgeGeneratorCommand.GenerateMethodBridge`方法和`ReversePInvokeWrapperGeneratorCommand.GenerateReversePInvokeWrapper`方法来生成桥接函数和反向PInvoke包装器。最后,调用`AOTReferenceGeneratorCommand.GenerateAOTGenericReference`方法来生成AOT泛型引用。

在执行完所有的构建命令后,脚本会将生成的DLL文件和AOT元数据DLL文件复制到指定的目录。这里使用了`File.Copy`方法来实现复制。复制完成后,脚本会打印出复制成功的消息。

接下来,脚本会将生成的DLL文件和AOT元数据DLL文件添加到Unity的Addressable Assets系统中。这里使用了`AddressableAssetSettings`类来实现。首先通过`AddressableAssetSettingsDefaultObject.Settings`属性获取到Addressable Assets的设置对象,然后通过`FindGroup`方法找到指定的Group,接着使用`CreateOrMoveEntry`方法创建或移动Asset Entry,并将其添加到指定的Group中。最后,通过设置Entry的标签和地址来完成添加操作。

当然这一通操作完,得必须SetDirty以通知Unity这块有改动。

/// <summary>
    /// 将热更DLL加入到Addressables
    /// </summary>
    /// <param name="dllPath">DLL完整路径</param>
    private static void SetHotUpdateDllLabel(string dllPath)
    {
        var settings = AddressableAssetSettingsDefaultObject.Settings;
        AddressableAssetGroup group = settings.FindGroup("DLLs");
        var guid = AssetDatabase.AssetPathToGUID(dllPath);
        if (settings.FindAssetEntry(guid) != null)
        {
            Console.WriteLine(
                $"[JenkinsBuild.SetHotUpdateDLLLabel] {dllPath} already exist in Addressables. Abort!"
            );
            return;
        }
        var entry = settings.CreateOrMoveEntry(guid, group);
		entry.labels.Add("default");
        entry.labels.Add("HotUpdateDLL");
        entry.address = Path.GetFileName(dllPath);
        settings.SetDirty(AddressableAssetSettings.ModificationEvent.EntryMoved, entry, true);
    }

    /// <summary>
    /// 将AOT元数据DLL加入到Addressables
    /// </summary>
    /// <param name="dllPath">DLL完整路径</param>
    private static void SetAOTMetadataDllLabel(string dllPath)
    {
        var settings = AddressableAssetSettingsDefaultObject.Settings;
        AddressableAssetGroup group = settings.FindGroup("DLLs");
        var guid = AssetDatabase.AssetPathToGUID(dllPath);
        if (settings.FindAssetEntry(guid) != null)
        {
            Console.WriteLine(
                $"[JenkinsBuild.SetAOTMetadataDLLLabel] {dllPath} already exist in Addressables. Abort!"
            );
            return;
        }
        var entry = settings.CreateOrMoveEntry(guid, group);
		entry.labels.Add("default");
        entry.labels.Add("AOTMetadataDLL");
        entry.address = Path.GetFileName(dllPath);
        settings.SetDirty(AddressableAssetSettings.ModificationEvent.EntryMoved, entry, true);
    }

最后,脚本会调用`buildAddressableContent`方法来构建Addressable Assets的内容。这里使用了`AddressableAssetSettings.BuildPlayerContent`方法来实现构建。构建完成后,脚本会打印出构建结果,并返回构建是否成功的标志。

private static bool buildAddressableContent()
    {
        string path = Path.Combine(Application.dataPath, "../ServerData/"+Enum.GetName(typeof(BuildTarget), EditorUserBuildSettings.activeBuildTarget));
        if(Directory.Exists(path)){
            Directory.Delete(path, true);
        }

        AddressableAssetSettings.BuildPlayerContent(out AddressablesPlayerBuildResult result);
        bool success = string.IsNullOrEmpty(result.Error);

        if (!success)
        {
            Console.WriteLine("[JenkinsBuild.buildAddressableContent] Addressables build error encountered: " + result.Error);
        }
        return success;
    }

以上就是这个脚本的主要功能和实现逻辑。通过这个脚本,我们可以方便地在Jenkins中构建Unity项目的热更新,并将生成的DLL文件和AOT元数据DLL文件添加到Unity的Addressable Assets系统中。

命令行参数样例

Unity.exe -nographics -batchmode -quit -executeMethod JenkinsBuild.BuildHotUpdateForWindows64

这个会只打热更新,不会打完整客户端。

更多Unity编辑器命令行参数,请查阅官方文档
Unity - Manual: Unity Editor command line arguments

希望这篇博客能给你带来一些思路。

(理论上我应该出个图文讲解视频,但是Hmm,我实在是在视频剪辑这块拉夸的一批)文章来源地址https://www.toymoban.com/news/detail-822023.html

到了这里,关于【Unity实战】Jenkins打包机联调脚本的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Unity实战】Jenkins与自定义命令行参数

    Unity是一款功能强大的游戏开发引擎,它提供了许多方便开发者使用的功能和工具。其中一个非常有用的功能是Unity命令行参数。通过使用命令行参数,开发者可以在启动Unity时自定义一些行为和设置,从而更好地管理和调试项目。 使用Unity命令行参数的好处是,它可以帮助开

    2024年01月19日
    浏览(38)
  • Unity实战篇 | 使Unity打包的exe程序始终保持屏幕最前端【文末送书】

    🎬 博客主页:https://xiaoy.blog.csdn.net 🎥 本文由 呆呆敲代码的小Y 原创,首发于 CSDN 🙉 🎄 学习专栏推荐:Unity系统学习专栏 🌲 游戏制作专栏推荐:游戏制作 🌲Unity实战100例专栏推荐:Unity 实战100例 教程 🏅 欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正! 📆 未来很长

    2023年04月11日
    浏览(42)
  • unity使用vs和手机联调

    其它问题 因为我们项目需要桥接安卓的地方,导致安卓那边的消息在unity这边看不到 这时候还是需要依赖Android Studio的log信息

    2024年02月11日
    浏览(25)
  • 【unity实战】一个通用的FPS枪支不同武器射击控制脚本

    实现FPS枪支不同武器效果,比如手枪,喷子,狙击枪,机枪,其实我最开始的想法是先做一个基类脚本,写一些公共属性和方法,然后再起不同的武器脚本这个基础基类,实现不同的武器效果。 这样的实现思路其实是没什么问题的,直到我看到这个视频:https://www.youtube.com

    2024年02月04日
    浏览(45)
  • Jenkins自动化打包脚本

    jenkins可以设置定时任务打包,也已手动点按钮打包,还可以通过执行http请求打包,今天我们就通过shell脚本,通过curl命令进行jenkins打包。 2.1 在jenkins上构建项目 设置触发器

    2024年02月13日
    浏览(43)
  • 【学习笔记】Rider调试unity【 联调、断点调试等】(决定弃用vscode了)

    转载请注明出处:🔗https://blog.csdn.net/weixin_44013533/article/details/130518705 小伙伴应该都尝试过vscode的unity调试插件了吧 插件不再维护不说,压根用不了,网上一堆抱怨,各种方法层出不穷,尝试了很多方法,正如这位网友所说的那样 插件官方也在github做出了回应 简单来说开发者

    2024年02月09日
    浏览(36)
  • Unity 之 Addressable可寻址系统 -- HybridCLR(华佗)+Addressable实现资源脚本全热更 -- 实战(二)

    在Unity中,结合 Addressable Asset System (简称:AA)和 HybridCLR 来实现热更新资源和脚本的控制。 AA 是Unity的一个强大的资源管理系统,可以用于动态加载和更新资源。HybridCLR是一个用于在Unity中运行C#脚本的工具,可以实现热更新脚本的功能。 使用版本: Unity 2022.3.8 Addressables 1.21

    2024年01月24日
    浏览(64)
  • Jenkins CI/CD 持续集成专题三 Jenkins 使用shell脚本打包组件配置流程

    第六步 查看编译状态和产物 到这里,jenkins 配置shell脚本打包组件的完整配置流程就已经完成

    2024年04月29日
    浏览(59)
  • Unity入门03——Unity脚本

    1.创建规则 不在VS中创建脚本了 可以放在Assets文件夹下的任何位置(建议同一文件夹管理) 类名和文件名必须一致,不然不能挂载(因为反射机制创建对象,会通过文件名去找Type) 建议不要使用中文名命名 没有特殊需求不用管命名空间 创建的脚本默认继承MonoBehavior 2.MonoBehavior基

    2023年04月09日
    浏览(52)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包