本文是个笔记,记录 2 个编辑器下编译程序集的逻辑。
一 . 编译 Runtime 用的程序集
Runtime 用的程序集:特征是会剔除 #if UNITY_EDITOR
注释的逻辑块)
public static void OnAssemblyBuildRequired()
{
// todo 目前是全部都编译,有木有方法只编译需要的 程序集呢?
var buildDir = Application.temporaryCachePath;
var files = new DirectoryInfo(buildDir).GetFiles();
foreach (var file in files)
{
FileUtil.DeleteFileOrDirectory(file.FullName);
}
var target = EditorUserBuildSettings.activeBuildTarget;
var group = BuildPipeline.GetBuildTargetGroup(target);
ScriptCompilationSettings scriptCompilationSettings = default;
scriptCompilationSettings.group = group;
scriptCompilationSettings.target = target;
ScriptCompilationResult scriptCompilationResult = PlayerBuildInterface.CompilePlayerScripts(scriptCompilationSettings, buildDir);
var lib_dir = Path.Combine(Application.dataPath, "..", "Library\\ScriptAssemblies");
foreach (var item in Instance.assemblies)
{
if (item.IsValid) // 如果配置正确则尝试转存储文件
{
var file = new FileInfo(Path.Combine(lib_dir, item.Dll));
var lastWriteTime = file.LastWriteTime.Ticks;
if (item.lastWriteTime < lastWriteTime)
{
item.lastWriteTime = lastWriteTime;
FileUtil.ReplaceFile(Path.Combine(buildDir, item.Dll), item.OutputPath);
item.UpdateInformation();
}
}
else
{
Debug.LogError($"{nameof(AssemblyHotfixManager)}: 请先完善 Hotfix Configuration 配置项!");
}
}
EditorUtility.SetDirty(Instance);
}
- 关键 API :
PlayerBuildInterface.CompilePlayerScripts(scriptCompilationSettings, buildDir)
- Build App 使用的这个API
- Addressable 也是这个API
二 . 编译指定 .asmdef 定义的程序集
public static void SyncAssemblyRawData(bool forceCompilie = false)
{
// 1. dll 编译存放处
var lib_dir = Path.Combine(Application.dataPath, "..", "Library\\ScriptAssemblies");
foreach (var item in Instance.assemblies)
{
// 如果配置正确则开始尝试编译
if (item.IsValid)
{
var data = JsonUtility.FromJson<SimplifiedAssemblyData>(item.assembly.text);
var dll = Path.Combine(lib_dir, $"{data.name}.dll");
var temp = Path.Combine(Application.temporaryCachePath, $"{data.name}.bytes");
var file = new FileInfo(dll);
if (file.Exists)
{
var lastWriteTime = file.LastWriteTime.Ticks;
if (forceCompilie || item.lastWriteTime < lastWriteTime)
{
item.lastWriteTime = lastWriteTime;
var assembly = CompilationPipeline.GetAssemblies(AssembliesType.Player).FirstOrDefault(v => v.name == item.assembly.name);
if (null != assembly)
{
// Compiles scripts outside the Assets folder into a managed assembly that can be used inside the Assets folder.
AssemblyBuilder builder = new AssemblyBuilder(temp, assembly.sourceFiles);
builder.compilerOptions.AllowUnsafeCode = item.AllowUnsafeCode;
BuildTargetGroup buildTargetGroup = BuildPipeline.GetBuildTargetGroup(EditorUserBuildSettings.activeBuildTarget);
builder.compilerOptions.ApiCompatibilityLevel = PlayerSettings.GetApiCompatibilityLevel(buildTargetGroup);
builder.additionalReferences = assembly.allReferences;
builder.flags = AssemblyBuilderFlags.None;
// todo: 以下动作会导致程序集完成编译后IDE 报错,待确认:减少编译频率,变成 aa build 时编译,同时看能不能将 csproj 回到原点
builder.referencesOptions = ReferencesOptions.UseEngineModules;
builder.buildTarget = EditorUserBuildSettings.activeBuildTarget;
builder.buildTargetGroup = buildTargetGroup;
builder.excludeReferences = new string[] { assembly.outputPath };
builder.additionalDefines = assembly.defines;
builder.compilerOptions = assembly.compilerOptions;
builder.buildFinished += (arg1, arg2) =>
{
bool noErr = true;
foreach (var msg in arg2)
{
if (msg.type == CompilerMessageType.Error)
{
Debug.LogError(msg.message);
noErr = false;
}
else if (msg.type == CompilerMessageType.Warning)
{
Debug.LogWarning(msg.message);
}
else
{
Debug.Log(msg.message);
}
}
if (noErr)
{
FileUtil.ReplaceFile(temp, item.OutputPath);
item.UpdateInformation();
}
};
if (!builder.Build())
{
Debug.LogError($"{nameof(AssemblyHotfixManager)}: Assembly {item.Dll} Build Fail!");
}
else
{
Debug.Log($"{nameof(AssemblyHotfixManager)} 热更程序集: <color=yellow>{item.Dll} </color> 完成构建!");
}
}
}
}
}
else
{
Debug.LogError($"{nameof(AssemblyHotfixManager)}: 请先完善 Hotfix Configuration 配置项!");
}
EditorUtility.SetDirty(Instance);
AssetDatabase.Refresh();
}
}
- 关键 API:
AssemblyBuilder.referencesOptions = ReferencesOptions.UseEngineModules;
编译为 Runtime 程序集 - 这个类主要用于将 Assets 文件夹以外的 Assembly 进行编译并放回到 Assets 中使用
- 这个类使用的编译器不同于 PlayerBuildInterface.CompilePlayerScripts ,编译时会修改所有的 .sln 和 .csproj 文件
- 上述代码并不完善,剩余部分在:这里
总结说明:
- 用 Assembly Definition File 定义的程序集在 Unity 中会编译成编辑器下使用的 dll 和 打包后使用的 dll 这 2种情况,编辑器环境加载的多为第一种,后者则是在用户 Build app 或者可寻址 build 时才会触发构建。
- 这两种情况的程序集,在 Unity 内部的概念种分别叫:EditorAssemblies 、PlayerAssembles;
- 这俩程序集最大特点是宏
#if UNITY_EDITOR
包裹的逻辑是否被剔除,以及是否引使用了EngineModules
. - 按照自己的理解,有朋友会将 PlayerAssemblies 说成"Runtime 用的 dll ",或者说成 “Release 版本的 dll ”,虽五花八门,但经得起推敲哈。
- 当然,可以自己使用 Roslyn 编译哈,别问,问就是可行,我朋友试过了~
网友评论