MSBuild v14在极少数情况下编译语义错误的程序集

Dáv*_*zky 10 c# msbuild binary compilation roslyn

在构建环境更新后,我们的一个Smoke Tests在TeamCity中爆发.调查结果显示,来自相同的源代码,

  • C:\ Windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe生成正确的二进制文件
  • C:\ Program Files(x86)\ MSBuild\14.0\bin\MSBuild.exe生成错误的二进制文件

什么时候发生

  • "params object []"被使用
  • 只传递一个值,而不是显式地包装在数组中
  • 使用命名参数
  • 与方法签名中的顺序不同

用于重现它的示例代码

static void Main(string[] args)
{
    var customerId = Guid.NewGuid();

    // Produces buggy code when compiled with MSBuild v14
    TestMethodWithParams(args: customerId, whatever: "foo");

    //All the calls below result correct behavior, regardless of the version of MSBuild, order and naming of parameters
    TestMethodWithParams("foo", customerId);
    TestMethodWithParams(whatever: "foo", args: customerId);

    TestMethodWithParams(args: new object[] { customerId }, whatever: "foo");
    TestMethodWithParams("foo", new object[] { customerId });
    TestMethodWithParams(whatever: "foo", args: new object[] {customerId});
}

private static void TestMethodWithParams(string whatever, params object[] args)
{
    Console.WriteLine("args: '{0}'", args);
}
Run Code Online (Sandbox Code Playgroud)

究竟发生了什么

不正确的版本只是吞下单个参数,传递null.反编译代码显示了差异:

在正确的二进制:

Guid guid = Guid.NewGuid();
Program.TestMethodWithParams("foo", new object[]
{
    guid
});
Run Code Online (Sandbox Code Playgroud)

在二进制文件中:

Guid guid = Guid.NewGuid();
object obj;
Program.TestMethodWithParams("foo", new object[]
{
    obj // <- this is and will always be null
});
Run Code Online (Sandbox Code Playgroud)

如何解决它

当我们将单个参数包装到对象数组中时,问题就消失了.另一种选择是不使用命名参数,和/或确保参数的出现顺序在调用和签名中是相同的.

但是:主要问题是我们无法恢复到较旧的MSBuild(...),并且检查整个代码库(并且通过我们的NuGet软件包中的每个二进制文件)也不是一个简单有效的解决方案.而且,这种错误可能会在以后的任何时候意外地重新引入代码库.因此,最好的解决方案可能是以某种方式修复MSBuild.

有没有人经历过这样的事情?可能是MSBuild中的一个错误?想法?