Unity 调用 gradle task

发布时间 2023-04-12 15:48:57作者: lac_123_1234

目标 & 背景

Unity Andorid 多渠道管理 这篇文章中,有提到如何对 Android 多渠道打包进行合理的自动化划分,但是仍然有一个核心问题没有解决,如何在 Unity CI/CD 流程中,直接调用 gradle 中写好的 task

我们的目标是,所有打包流程全部在 Unity 中完成,不管用的是什么平台的 CI/CD 流程,最终仅仅是调用了 Unity 中一个静态方法

这样无论当前机器环境如何变化,一定可以打出正确的包。相比一些将部分打包流程写在对应平台的 CI/CD 中的方案,更灵活一些

gradle 环境问题

C# 在不同环境下调用 shell 脚本 这篇文章中,提到了使用 CliWrap 直接调用外部指令,但如果直接运行 gradle xxxtask 有可能会直接报错

这是因为 gradle 依赖当前环境中的 java 版本,同时通过 CliWrap 调用的 gradle 也很有可能跟你项目应该使用的 gradle 版本不一致,这样一来问题就变得复杂了

前面提到,我们希望在任何机器上都可以正确打包,可能打包的机器是 Windows 也可能是 Mac,他们各自的 PATH 中对应的 java 版本不一致,gradle 版本也不一致

在导出 Android Studio 项目后,会自动在 gradle/wrapper/gradle-wrapper.properties 这个文件下生成使用的 gradle 版本,比如下方的 distributionUrl 中对应的 6.1.1 就是当前使用的版本

distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

这里的 gralde 会根据版本自动下载并存放在 ~.gradle/wrapper/dists 目录下,但如果自己去写这里的处理过程还是太麻烦了,为了解决这个问题谷歌提供了一个通用的 task

gradle wrapper

当你执行这个任务后,会在当前项目的根目录生成 gradlew.batgradlew 脚本文件,通过这两个文件就可以执行当前项目对应的 gradle

gradlew xxxtask

但是问题到了这里就开始离谱了,我需要 gradlew 文件才能正确找到 gradle,但是 gradlew 需要使用正确的 gradle 生成,这样问题就变成了到底先有 只因 还是先有

为什么 Unity 可以直接生成 apk

在我一筹莫展时,突然想到如果打包时直接选择导出 apk,通过 Unity 执行时却可以正确执行 gradle task,那么 Unity 安装的安卓打包环境中一定有对应的环境解决方案

xxxUnity/PlaybackEngines/AndroidPlayer 文件夹中可以找到 NDKJDKSDK 对应的目录,因此 JAVA_HOME 就特指 JDK 的目录,而 Unity 也提供了 gradle-cli.jar 文件,存放在如下路径 /Tools/gradle/lib/gradle-cli-6.1.1.jar,到这里问题就比较简单了

解决方案

为了解决获取 wrapper 文件的问题,我们要拿到当前 Unity 的 JAVA_HOME 以及 gradle-cli.jar 的路径,通过 java 执行 gradle-cli.jar 文件即可,幸运的是这个问题已经有人解决过了 unity-gradle-wrapper

在这个仓库中,帮你已经定义好了 IPostGenerateGradleAndroidProject,打包时会自动生成 gradlew.batgradlew 文件

那么现在我们只需要控制好整体的打包生命周期和顺序,在 安卓项目导出后,直接调用当前环境的 wrapper 文件执行对应的 task 即可

string exe = "cmd";
string gradlew = "/c \"gradlew.bat\"";

#if UNITY_EDITOR_OSX
exe     = "/bin/bash";
gradlew = "./gradlew";
#endif

var cli = Cli.Wrap(exe).WithWorkingDirectory("安卓项目路径").
        WithArguments("{gradlew} xxxTask").
        WithValidation(CommandResultValidation.ZeroExitCode) |
                      (Debug.Log, Debug.LogError);

 var success = (await cli.ExecuteAsync()).ExitCode == 0;