MSBuild v14 在极少数情况下会编译语义不正确的程序集
MSBuild v14 compiles a semantically incorrect assembly under some rare circumstances
构建环境更新后,我们的一项冒烟测试在 TeamCity 中出现故障。调查结果表明,从相同的源代码,
- C:\Windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe 生成正确的二进制文件
- C:\Program Files (x86)\MSBuild.0\bin\MSBuild.exe 生成不正确的二进制文件
什么时候出现
- 使用“参数对象[]”
- 只传递一个值,没有显式包装在数组中
- 使用命名参数
- 与方法签名中的顺序不同
重现它的示例代码
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);
}
到底发生了什么
不正确的版本只是吞掉了单个参数,正在传递null。反编译代码显示差异:
正确的二进制格式:
Guid guid = Guid.NewGuid();
Program.TestMethodWithParams("foo", new object[]
{
guid
});
二进制文件不正确:
Guid guid = Guid.NewGuid();
object obj;
Program.TestMethodWithParams("foo", new object[]
{
obj // <- this is and will always be null
});
如何修复
当我们将单个参数包装到一个对象数组中时,问题就消失了。另一种选择是不使用命名参数,and/or 确保参数在调用和签名中出现的顺序相同。
但是:主要问题是我们无法恢复到较旧的 MSBuild (...),并且检查整个代码库(并检查我们 NuGet 包中的每个二进制文件)也不是一个简单有效的解决方案。此外,这种错误可能会在以后的任何时候意外地重新引入代码库。因此,最好的解决方案可能是以某种方式修复 MSBuild。
有没有人经历过这样的事情?可能是 MSBuild 中的错误?想法?
正如我在 GitHub 问题中提到的那样,我相信这是最初报告为 #4197 并在 Roslyn 1.1 中修复的错误。
感谢您提供的所有信息和建议。您分享了帮助我们追踪此问题的详细信息。原来安装 "Microsoft Build Tools 2015 with Update 2" 解决了我们的问题。
再次感谢大家。
/大卫
构建环境更新后,我们的一项冒烟测试在 TeamCity 中出现故障。调查结果表明,从相同的源代码,
- C:\Windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe 生成正确的二进制文件
- C:\Program Files (x86)\MSBuild.0\bin\MSBuild.exe 生成不正确的二进制文件
什么时候出现
- 使用“参数对象[]”
- 只传递一个值,没有显式包装在数组中
- 使用命名参数
- 与方法签名中的顺序不同
重现它的示例代码
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);
}
到底发生了什么
不正确的版本只是吞掉了单个参数,正在传递null。反编译代码显示差异:
正确的二进制格式:
Guid guid = Guid.NewGuid();
Program.TestMethodWithParams("foo", new object[]
{
guid
});
二进制文件不正确:
Guid guid = Guid.NewGuid();
object obj;
Program.TestMethodWithParams("foo", new object[]
{
obj // <- this is and will always be null
});
如何修复
当我们将单个参数包装到一个对象数组中时,问题就消失了。另一种选择是不使用命名参数,and/or 确保参数在调用和签名中出现的顺序相同。
但是:主要问题是我们无法恢复到较旧的 MSBuild (...),并且检查整个代码库(并检查我们 NuGet 包中的每个二进制文件)也不是一个简单有效的解决方案。此外,这种错误可能会在以后的任何时候意外地重新引入代码库。因此,最好的解决方案可能是以某种方式修复 MSBuild。
有没有人经历过这样的事情?可能是 MSBuild 中的错误?想法?
正如我在 GitHub 问题中提到的那样,我相信这是最初报告为 #4197 并在 Roslyn 1.1 中修复的错误。
感谢您提供的所有信息和建议。您分享了帮助我们追踪此问题的详细信息。原来安装 "Microsoft Build Tools 2015 with Update 2" 解决了我们的问题。
再次感谢大家。 /大卫