waf编译框架
Unity, as you may know, helps you build for many different platforms, be it consoles, computers, mobiles or XR. In order to do this, it uses things like a compiler, specific compiler flags, various SDKs, third party libraries and maybe even a specific OS required by the platforms. We call this combination of software strung together a Toolchain.
您可能知道,Unity可以帮助您针对许多不同的平台进行构建,无论是控制台,计算机,移动设备还是XR。 为了做到这一点,它使用诸如编译器,特定的编译器标志,各种SDK,第三方库,甚至平台所需的特定OS之类的东西。 我们称这种串在一起的软件为工具链。
So as consoles, VR headsets, phone or smart tv OS are introduced or made obsolete, or just updated, we need to tinker with these Toolchains and/or make necessary code changes to the Unity codebase to keep everything working as intended.
因此,随着控制台,VR头戴式耳机,电话或智能电视OS的引入,过时或刚刚更新,我们需要修改这些工具链和/或对Unity代码库进行必要的代码更改,以使所有功能按预期运行。
我们要解决什么? (What Are We Trying to Solve?)
As with all software there is, more often than you’d think, unexpected behaviour. This could be everything from bugs to undocumented behaviour. Compilers and SDKs aren’t immune to things such as these either, and like any other software they’re also affected by software updates. You may have undocumented behaviour you’re relying on change suddenly or bugs being fixed and/or introduced as you perform an update. Add to this that different platforms may support wildly different feature sets, with some being bleeding edge and others more archaic.
与所有软件一样,出乎意料的是,往往比您想象的要多。 从错误到未记录的行为,这无所不包。 编译器和SDK也不是不会受到诸如此类的影响,并且像其他任何软件一样,它们也受到软件更新的影响。 您可能会出现一些未记录的行为,您可能会突然依赖于更改,或者在执行更新时已修复和/或引入了错误。 除此之外,不同的平台可能支持截然不同的功能集,其中一些功能非常先进,而另一些则更古老。
Having some order in this and keeping track of changes seems like a good thing.
在其中进行一些排序并跟踪更改似乎是一件好事。
Over the years there are many scenarios we handle for our Toolchains, such as bugs in specific compilers or certain toolchains lacking some compiler features. Even the now somewhat old C++11 isn’t something we can use across our entire code base, since simply some of our platforms do not, and can’t support it.
多年来,我们为工具链处理的场景很多,例如特定编译器中的错误或缺少某些编译器功能的某些工具链。 即使是现在已经有些陈旧的C ++ 11,我们也无法在整个代码库中使用,因为仅我们的某些平台不支持也不支持。
Based on the state of these different toolchains at one point in time, we need to take different strategies.
根据某一时刻这些不同工具链的状态,我们需要采取不同的策略。
This means it would be possible for these fixes to work around a problem, to long outlive the issues themselves and remain left in the code, warning future people of a problem that really isn’t a problem anymore.
这意味着这些修补程序有可能解决问题,使问题本身长期存在并保留在代码中,从而警告未来的人们确实不再是问题的问题。
So we need to stay on top of what is fixed, broken, supported and unsupported for our Toolchains as they get added, updated or left by the wayside.
因此,当我们的工具链被添加,更新或遗留在路边时,我们需要紧跟其固定,损坏,受支持和不受支持的内容。
Another issue is the round-trip time for our developers who are working at the lower levels of our code base. Today if they’re, for example, making some new macros and want to be sure it will work across all our Toolchains, they end up having to build the editor on our build system for each platform just to find out if it will compile. That is a very time consuming and a lot of wasted resources for our build farm just to see if a macro will compile or not.
另一个问题是在较低代码层工作的开发人员的往返时间。 今天,例如,如果他们要制作一些新的宏,并想确保它可以在我们所有的工具链中使用,那么他们最终不得不在我们的构建系统上为每个平台构建编辑器,只是想知道它是否可以编译。 仅仅查看宏是否将编译,这对于我们的构建场来说是非常耗时的,并且浪费了大量资源。
So this is also something we want to try to improve.
因此,这也是我们要尝试改进的方面。
什么是编译器测试框架? (What is The Compiler Test Framework?)
It’s a series of C# NUnit tests written as small C or C++ snippets that get compiled against all our Toolchains. Each test contains one code snippet which asserts on the behaviour we’re expecting. Even if this behaviour is in fact a bug and doesn’t compile, this is still a behaviour we expect and will write a test for it. So if down the line it’s fixed for a compiler or an SDK update, we will be notified, and maybe that will mean we can finally use that compiler feature across all our platforms.
这是一系列用小型C或C ++代码段编写的C#NUnit测试,并针对我们所有的工具链进行了编译。 每个测试包含一个代码段,这些代码段声明了我们期望的行为。 即使此行为实际上是一个错误且无法编译,也仍然是我们期望的行为,并将为此编写测试。 因此,如果将其固定用于编译器或SDK更新,我们将收到通知,也许这意味着我们最终可以在所有平台上使用该编译器功能。
The framework for now only compiles the snippets, so it won’t actually run the binary it generates or in any way analyses it. So there can be no runtime assertions done in the tests, only static ones.
现在,该框架仅编译代码片段,因此它实际上不会运行它生成的二进制文件或以任何方式对其进行分析。 因此,测试中不能有任何运行时断言,只有静态断言。
The compiler tests utilize our build system, which is made in C#, for all the heavy lifting, and lets it run the build for our Toolchains and uses the snippet in the test as the source files for it.
编译器测试使用了C#中的构建系统来完成所有繁重的工作,并使其运行我们的工具链的构建,并将测试中的代码片段用作其源文件。
Doing this lets us assert on compiler output, including build errors and warnings. So we can do things like saying “This code works for all our Toolchains except for these two, and for these two it will print this error message” and mark the test as successful as long as this remains true.
这样做使我们可以断言编译器的输出,包括构建错误和警告。 因此,我们可以做一些事情,例如说“此代码对我们的所有工具链都适用,除了这两个之外,对于这两个工具,它将打印此错误消息”,并在测试保持成功的情况下将测试标记为成功。
You may think it’s weird that a build failure should still let a test succeed, but the compiler tests are all about writing down our assumptions about the toolchains.
您可能会认为构建失败仍然应该使测试成功是很奇怪的,但是编译器测试都是关于写下我们对工具链的假设的。
If we know that a specific feature or corner case currently isn’t supported on one platform, we make sure there is a test added which says so.
如果我们知道一个平台当前不支持特定功能或特殊情况,请确保添加了一个测试,说明是否如此。
This means that we can easily look up in our code which features are supported by our Toolchains. However, it will also let us know if this actually changes at some point as we update our Toolchains.
这意味着我们可以轻松地在我们的代码中查找工具链支持的功能。 但是,它也会让我们知道在更新工具链时,它是否确实在某个时候改变了。
This will also help the low level developers with their round-trip time for testing that changes compile on all platforms and that any compile time assertions are valid.
这还将帮助低级开发人员往返时间,以测试更改是否已在所有平台上编译以及所有编译时间声明是否有效。
The build system knows about our code base, so the developers can include the header files for what they have implemented and do extra assertions if necessary in the test. This also allows them to explicitly test for things that shouldn’t compile, which our normal builds wouldn’t allow for obvious reasons.
构建系统了解我们的代码库,因此开发人员可以包含其已实现内容的头文件,并在测试中必要时进行其他断言。 这也使他们可以显式测试不应该编译的内容,出于明显的原因,我们的常规版本不允许这样做。
在实践中 (In Practice)
Take the example below which is one of our tests:
以下面的示例为例,这是我们的测试之一:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
namespace Unity.CompilerTests.Tests
{
[CompilerFeatureName("Macro behavior consistency")]
[CompilerFeatureDescription("Does various macro checks to ensure they behave the same on all toolchains")]
public class MacroTests : CompilerTestBase
{
[CompilerTest]
public void MacroExpansions()
{
var code = PrepareCode(
"const int _Q = 1;",
"#define PP_CAT(a, b) a##b",
"#define PP_CAT2(a, b) PP_CAT(a,b)",
"#define PP_CAT3(a, b) PP_CAT2(a,b)",
"#define E(a) QQ a",
"#define QQ() Q",
"#define T2() PP_CAT2(_,E(()))",
"#define T3() PP_CAT3(_,E(()))",
"int function()",
"{",
" if (T2() == T3())",
" return 18;",
" return 42;",
"}");
var result = Compile(code);
result.ShouldSucceed(outcomeException: () =>
{
// VisualStudioToolChain is the only toolchain that does not support this scenario,
// but we do expect it so as long as we get it we fail it.
// If this suddenly starts working in the future (by this test failing)
// we need to check over our macros and see if any specific workarounds can be removed.
if (toolChain is VisualStudioToolChain)
return "error C3861: \'_QQ\': identifier not found";
return null;
});
}
}
}
|
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
namespace Unity . CompilerTests . Tests
{
[ CompilerFeatureName ( "Macro behavior consistency" ) ]
[ CompilerFeatureDescription ( "Does various macro checks to ensure they behave the same on all toolchains" ) ]
public class MacroTests : CompilerTestBase
{
[ CompilerTest ]
public void MacroExpansions ( )
{
var code = PrepareCode (
"const int _Q = 1;" ,
"#define PP_CAT(a, b) a##b" ,
"#define PP_CAT2(a, b) PP_CAT(a,b)" ,
"#define PP_CAT3(a, b) PP_CAT2(a,b)" ,
"#define E(a) QQ a" ,
"#define QQ() Q" ,
"#define T2() PP_CAT2(_,E(()))" ,
"#define T3() PP_CAT3(_,E(()))" ,
"int function()" ,
"{" ,
" if (T2() == T3())" ,
" return 18;" ,
" return 42;" ,
"}" ) ;
var result = Compile ( code ) ;
result . ShouldSucceed ( outcomeException : ( ) = >
{
// VisualStudioToolChain is the only toolchain that does not support this scenario,
// but we do expect it so as long as we get it we fail it.
// If this suddenly starts working in the future (by this test failing)
// we need to check over our macros and see if any specific workarounds can be removed.
if ( toolChain is VisualStudioToolChain )
return "error C3861: \'_QQ\': identifier not found" ;
return null ;
} ) ;
}
}
}
|
What you see here is an unexpected behaviour we found with the MSVC compiler.
您在此处看到的是我们在MSVC编译器中发现的意外行为。
If we compile the code in the snippet, it will fully expand the macro on all our Toolchains, except for our Visual Studio Toolchain.
如果我们在代码段中编译代码,它将完全扩展除我们的Visual Studio工具链以外的所有工具链上的宏。
As you can see in the comments in the code, we have made some assumptions in our macros elsewhere based on this behaviour. So if this test starts failing for future Visual Studio versions, by the compilation succeeding (strange huh?), we can get rid of this special consideration in our macros, so we don’t have to maintain this behaviour anymore.
正如您在代码的注释中所看到的,基于此行为,我们在其他宏中进行了一些假设。 因此,如果此测试对于将来的Visual Studio版本开始失败,则通过成功的编译(奇怪吗?),我们可以在宏中摆脱这种特殊的考虑,因此我们不再需要保持这种行为。
This holds true for other toolchains as well. Just for C++ language features we can use there is a huge disparity between our different Toolchains. Some features we can’t use at all because a specific Toolchain doesn’t support it and some of these we have written our own emulations for. With these tests helping us keep track, maybe we can even go full C++11 some day, and drop many of the emulations we have been forced to create for some of our platforms.
其他工具链也是如此。 仅就C ++语言功能而言,我们可以使用的不同工具链之间存在巨大差异。 我们根本无法使用某些功能,因为特定的工具链不支持它,而其中一些我们已经编写了自己的仿真器。 通过这些测试,我们可以追踪,也许有一天我们甚至可以使用完整的C ++ 11,并放弃我们不得不为某些平台创建的许多仿真。
So for the framework we have created some attributes to facilitate this for us. The CompilerTestAttribute essentially just figures out which Toolchains are supported on the current OS and makes sure the test gets run multiple times with a different Toolchain selected each time.
因此,对于框架,我们创建了一些属性来为我们提供便利。 实际上,CompilerTestAttribute只是弄清楚当前操作系统支持哪些工具链,并确保每次选择不同的工具链就可以多次运行测试。
This is coupled to the CompilerTestBase class we inherit from, which just sets things up for the currently requested Toolchain to make sure Compile() only compiles for the correct toolchain and reports back the results.
这与我们从其继承的CompilerTestBase类耦合,该类只是为当前请求的工具链进行设置,以确保Compile()仅针对正确的工具链进行编译并报告结果。
We also have some custom documentation attributes which we use to generate a report for our tests. You can read more about this further down.
我们还有一些自定义文档属性,可用于为测试生成报告。 您可以进一步了解此内容。
Below you can see how it looks if I run the test on my Windows machine:
在下面,您可以查看在Windows计算机上运行测试时的外观:
It runs on our Visual Studio 2010 and 2015 (x86 and x64) and Emscripten toolchain. Since this is a Windows machine, it will only run the Toolchains supported on the current OS. So if I were to run this on my Mac, it would show our Mac Editor Toolchain.
它在我们的Visual Studio 2010和2015(x86和x64)和Emscripten工具链上运行。 由于这是Windows计算机,因此它将仅运行当前OS支持的工具链。 因此,如果我要在Mac上运行它,它将显示Mac编辑器工具链。
Our build runners on Katana are configured to run these tests for Mac, Windows and Linux, so it will cover all our Toolchains. If something changes in our Toolchains that we have covered in our tests, the build will fail and let us know. This also allows the developers to easily make sure that the tests they’re writing will work across all our supported Toolchains. As of right now, it takes around 8 minutes to have the test suite run for all the Toolchains we support (1 minute to checkout the code, 6 minutes to prepare and build the build system and 1 minute to run all build system tests).
我们在Katana上的构建运行器配置为针对Mac,Windows和Linux运行这些测试,因此它将涵盖我们所有的工具链。 如果我们测试中涉及的工具链中发生某些变化,构建将失败并告知我们。 这也使开发人员可以轻松地确保他们正在编写的测试可以在我们所有受支持的工具链中使用。 到目前为止,为我们支持的所有工具链运行测试套件大约需要8分钟(签出代码需要1分钟,准备和构建构建系统需要6分钟,而运行所有构建系统测试则需要1分钟)。
We also created a little tool that will generate some information on the current status of the compiler tests in trunk, which we then use to generate a report on an internal website as you can see here.
我们还创建了一个小工具,它将生成有关主干中的编译器测试的当前状态的一些信息,然后将其用于在内部网站上生成报告,如您在此处所见。
编译器测试报告网站 (Compiler Test Report Site)
Here you can see all the information about the tests we have now. We only have one case where it intentionally fails so far, and that is the Macro Expansions test for the Visual Studio toolchain that I showed you above.
在这里,您可以查看有关我们现有测试的所有信息。 到目前为止,只有一种情况有意失败,那就是我上面向您展示的针对Visual Studio工具链的宏扩展测试。
So our devs can just visit this page if they feel unsure to get a better picture of the situation. The custom documentation attributes I mentioned earlier were meant for this purpose. As you see below, you can hover over the various sections and get information about the individual entries.
因此,如果我们的开发人员不确定是否能更好地了解情况,则可以访问此页面。 我前面提到的自定义文档属性就是用于此目的的。 如下所示,您可以将鼠标悬停在各个部分上并获取有关各个条目的信息。
Each row in the report is the result for one test class, which may contain multiple tests. Each class should represent one high level feature.
报告中的每一行都是一个测试类的结果,其中可能包含多个测试。 每个类应代表一个高级功能。
They can also use the handy links on the right hand side to browse the test class in our code repository if they want to take a closer look at what is actually tested.
如果他们想仔细查看实际测试的内容,还可以使用右侧的便捷链接来浏览代码存储库中的测试类。
结论 (Conclusion)
You can see that in truth we don’t have that many tests and Toolchains added yet. This has much to do with the fact that the entire build system of Unity hasn’t been moved over to the new C# based one and the framework is highly dependent on this. So this is still very early days for the Compiler Test Framework. We hope it will steadily grow as our Toolchains are ported over to C#, until we have mapped out all the anomalies we have detected, as well as all compiler features we use and want to use. You can expect to see a future blog post on this with a status update.
您可以看到,实际上我们还没有添加太多的测试和工具链。 这与以下事实有关:整个Unity的构建系统尚未移交给基于C#的新系统,并且框架高度依赖于此。 因此,对于Compiler Test Framework来说,这还处于初期。 我们希望在将工具链移植到C#之前,它将稳定增长,直到我们绘制出我们检测到的所有异常以及我们使用和想要使用的所有编译器功能为止。 您可以期望在此之后看到有关状态更新的博客文章。
翻译自: https://blogs.unity3d.com/2018/01/04/compiler-test-framework/
waf编译框架