What is IL2CPP?
IL2CPP的技术有两个不同的部分:
1.AOT编译器:将中间语言(IL) 翻译成c++源代码。
2.运行时虚拟机:提供服务和抽象,比如垃圾收集器、独立于平台的线程和文件访问,以及内部调用的实现(直接修改托管数据结构的本地代码)。
The AOT compiler
IL2CPP AOT编译器被命名为il2cpp.exe。在Windows中,您可以在unity安装目录下的Editor\Data\il2cpp目录中找到它。在OSX上,它位于Unity安装目录下的Contents/Frameworks/il2cpp/build目录中。il2cpp.exe是一个完全用c#编写的托管的可执行文件。在IL2CPP的开发过程中,我们同时使用.net和Mono编译器对其进行编译。
il2cpp.exe接受由Unity附带的Mono编译器编译的托管程序集,并生成c++代码,我们将其传递给特定平台的c++编译器。
你可以这样考虑IL2CPP工具链:
The runtime library
IL2CPP技术的另一部分是支持虚拟机的运行时库。我们几乎完全使用c++代码实现了这个库。我们将运行时库称为libil2cpp,它作为一个静态库链接到exe(可执行文件)中。IL2CPP技术的关键好处之一是这个简单且可移植的运行时库。
你可以通过unity查看libil2cpp的头文件,来查看libil2cpp是如何编写的,Windows上,你可以在Editor\ Data \ PlaybackEngines \ webglsupport \ BuildTools \Libraries\ libil2cpp \include 文件夹下查看, OSX上,在Contents/Frameworks/il2cpp/libil2cpp 文件夹下 。例如,il2cpp.exe生成的c++代码与libil2cpp运行时之间的接口位于codegen/il2cpp-codegen.h头文件中。
runtime library的一个关键部分是垃圾收集器。Unity 5使用的是libgc (Boehm-Demers-Weiser垃圾收集器)。但是,libil2cpp被设计成允许我们使用其他垃圾收集器。例如,我们正在研究Microsoft GC的集成,它是开源的,作为CoreCLR的一部分。在本系列后面关于垃圾收集器集成的文章中,我们将对此进行更多讨论。
How is il2cpp.exe executed?
让我们看一个例子。我将在Windows上使用Unity 5.0.1,我将从一个新的空项目开始。这样我们至少有一个用户脚本转换,我将添加这个简单的MonoBehaviour组件到主摄像头游戏对象:
using UnityEngine;
public class HelloWorld : MonoBehaviour {
void Start () {
Debug.Log("Hello, IL2CPP!");
}
}
当我发布WebGL 平台时, 我可以使用 Process Explorer(进程资源管理器 - Sysinternals | Microsoft Learn)查看 Unity 运行 il2cpp.exe使用的命令,如下图所示:
"C:\Program Files\Unity\Editor\Data\MonoBleedingEdge\bin\mono.exe" "C:\Program Files\Unity\Editor\Data\il2cpp/il2cpp.exe" --copy-level=None --enable-generic-sharing --enable-unity-event-support --output-format=Compact --extra-types.file="C:\Program Files\Unity\Editor\Data\il2cpp\il2cpp_default_extra_types.txt" "C:\Users\Josh Peterson\Documents\IL2CPP Blog Example\Temp\StagingArea\Data\Managed\Assembly-CSharp.dll" "C:\Users\Josh Peterson\Documents\IL2CPP Blog Example\Temp\StagingArea\Data\Managed\UnityEngine.UI.dll" "C:\Users\Josh Peterson\Documents\IL2CPP Blog Example\Temp\StagingArea\Data\il2cppOutput"
这个命令行非常长,所以让我们分解一下。首先,Unity正在运行这个可执行文件mono.exe:
1 | "C:\Program Files\Unity\Editor\Data\MonoBleedingEdge\bin\mono.exe" |
命令行上的下一个参数是il2cpp.exe实用工具本身。
1 | "C:\Program Files\Unity\Editor\Data\il2cpp/il2cpp.exe" |
- –copy-level=None
- 指定 il2cpp.exe 不应该执行 C++ 生成代码的特殊文件副本.
- –enable-generic-sharing
- 这是一个减少代码和二进制大小的特性. IL2CPP 会共享通用代码的实现,当它可以这么做的时候
- –enable-unity-event-support
- 特殊支持,以确保通过反射访问的Unity事件的代码被正确生成。
- –output-format=Compact
- 以一种需要较少字符作为类型和方法名称的格式生成c++代码。它会去掉IL代码中的名称,比如方法名,变量名,但它通常编译得更快,因为有更少的代码供c++编译器解析。
- –extra-types.file=”C:\Program Files\Unity\Editor\Data\il2cpp\il2cpp_default_extra_types.txt”
- 使用默认的(空的)extra类型文件。这个文件可以添加到一个Unity项目中,让il2cpp.exe知道哪些泛型或数组类型将在运行时创建,但不在IL代码中。
需要注意的是,这些命令行参数在以后的版本中可以也将会更改。我们还没有为il2cpp.exe提供一组稳定且受支持的命令行参数。
最后,我们列出了两个文件和一个文件夹:
- “C:\Users\Josh Peterson\Documents\IL2CPP Blog Example\Temp\StagingArea\Data\Managed\Assembly-CSharp.dll”
- “C:\Users\Josh Peterson\Documents\IL2CPP Blog Example\Temp\StagingArea\Data\Managed\UnityEngine.UI.dll”
- “C:\Users\Josh Peterson\Documents\IL2CPP Blog Example\Temp\StagingArea\Data\il2cppOutput”
il2cpp.exe接受一个列表,里面包换了它需要转换的所有IL程序集。在本例中,它们是包含我simple脚本的assembly - csharp.dll。以及GUI程序集UnityEngine.UI.dll。注意,这里有一些明显缺失的dll。显然,我的脚本引用了UnityEngine.dll,它至少引用mscorlib.dll或其他程序集。他们在哪儿?实际上,il2cpp.exe在内部解析这些程序集。可以在命令行中添加上,但它们不是必须的
il2cpp.exe命令行上的最后一个参数是创建输出c++文件的目录。如果您感兴趣,可以查看该目录中生成的文件,它们将是本系列下一篇文章的主题。在你做之前,你需要发布WebGL时在build settings中设置“Development Player”选项。这将删除-output-format =Compact命令行参数,并在生成的c++代码中为您提供更好的类型和方法名称
尝试在WebGL或iOS播放器设置中改变各种选项。您应该能够看到传递给il2cpp.exe的不同命令行选项,以启用不同的代码生成步骤。例如,将WebGL播放器设置中的“启用异常”设置更改为“Full”值将向il2cpp.exe命令行添加- emit-null-check、- enablestacktrace和- enablearrays -bounds-check参数。
What does IL2CPP not do?
我们没有尝试用IL2CPP重写c#标准库。当构建一个使用IL2CPP的Unity项目时,所有的c#标准库代码都在mscorlib.dll中,system.dll等是完全相同的代码用于Mono脚本后端。
我们依赖于c#标准库代码,这些代码已经被用户熟知,并且在Unity项目中经过了良好的测试。因此,当我们调查与IL2CPP相关的错误时,我们可以相当有把握地认为,该错误不是在AOT编译器中,就是在运行时库中,而不是在其他地方。