Nuget 包不支持程序集版本的位数

Nuget pack does not honor number of digits on assembly version

我需要 nuget pack 来生成一个只有 3 位数字的包版本(我们想对其进行语义版本控制)但是当我在一个 AssemblyVersion 属性设置为的 csproj 上调用它时“1.0.0”,生成的 nupkg 文件在其元数据(和文件名)中以版本“1.0.0.0”结尾。为什么命令行工具不遵守 AssemblyVersion 属性中指定的位数?

我首先对 csproj 文件调用 nuget spec,它会生成一个像这样的存根 nuspec 文件(它实际上包含更多带有占位符值的标签,但我已经删除了它们,因为我们没有不需要它们):

<?xml version="1.0"?>
<package >
  <metadata>
    <id>$id$</id>
    <version>$version$</version>
    <title>$title$</title>
    <authors>$author$</authors>
    <owners>$author$</owners>
    <requireLicenseAcceptance>false</requireLicenseAcceptance>
    <description>$description$</description>
    <releaseNotes>Release notes.</releaseNotes>
    <copyright>Copyright 2015</copyright>
  </metadata>
</package>

在与 csproj 文件相同的文件夹中的 TFS 中检查此 nuspec 文件后,我们现在可以像这样调用 pack:

nuget pack MyProject.csproj

项目的 AssemblyInfo.cs 文件包含明确设置版本的行:

[assembly: AssemblyVersion("1.0.0")]

除了该工具在检索程序集版本时使用 4 位数字之外,一切都运行良好。当我在文件资源管理器上右键单击 dll 并转到详细信息时,即使 Windows 显示的版本也只有 3 位数字。为什么 NuGet 使用 4 位数字?我是否遗漏了一些明显的东西?

在 nuspec 中对版本进行硬编码显然并不理想,因为那样的话我们将不得不在两个不同的地方维护版本号,而它们本应始终相同。我的意思是,这应该是特殊占位符值 $version$ 背后的想法,NuGet 本身知道如何从项目中提取。

Creating and Publishing a Package 中指出,当使用标记 $version$ 时,NuGet 使用 AssemblyVersionAttribute 属性。
深入研究 NuGet 源代码,我发现它并不像人们想象的那么简单。

NuGet 使用反射来获取库的版本。 AssemblyName.Version 确切地说。由于版本的所有组件都必须是大于或等于零的整数(请参阅 AssemblyName.Version),因此显示的版本是 1.0.0.0(在您的情况下)而不是在 AssemblyVersion 中声明的 1.0.0属性。

可能的解决方案
Nuspec Reference 页面在 $version$ 标记旁边添加了更多信息。它提到 AssemblyInformationalVersionAttribute 属性将优先于 AssemblyVersionAttribute。使用它会解决你的问题。

深入挖掘
您可能想知道为什么 AssemblyInformationalVersionAttribute 有效而 AssemblyVersionAttribute 无效?

这个问题的答案是 NuGet 使用 CustomAttributeData.GetCustomAttributes(Assembly) 函数在使用 Assembly.Version 之前检索库的属性。上面的函数不会列出AssemblyVersionAttribute,但如果在程序集上使用它会列出AssemblyInformationalVersionAttribute。 只有这样,如果未找到 AssemblyInformationalVersionAttribute,才会使用 Assembly.Version。

编辑:
相关NuGet源码:
NuGet 使用以下代码获取程序集版本(如果未找到 AssemblyInformationalVersionAttribute):

Assembly assembly = Assembly.ReflectionOnlyLoadFrom(path);
AssemblyName assemblyName = assembly.GetName();
...
version = new SemanticVersion(assemblyName.Version);

问题始于 assembly.GetName(),因为它使用相关参数(包括版本)创建和初始化 AssemblyName。
(该代码可以在 AssemblyMetadataExtractor class 的 public AssemblyMetadata GetMetadata(string path) 函数下找到。)

运行 进入同样的问题并使用 nuget 的“-version”属性解决了它。它会覆盖 nuspec 文件中的版本。非常适合语义版本控制。

根据以上情况,我猜在开始讨论时它还不可用。

NuGet 似乎使用 [assembly: AssemblyInformationalVersion()] 作为版本,而不是 AssemblyVersionAssemblyFileVersion

AssemblyInformationalVersion 设置为 2 或 3 个组件,nuget 将完全按照您指定的方式打包它

我还将 AssemblyVersion 设置为一些 auto-incremented 东西,因为它被 .NET 使用,并且根据我的 MSBuild 经验,强制它思考版本更安全一直在变化,所以它不会试图变得聪明

[assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyInformationalVersion("1.0")]

结果

Project.1.0.nupkg

含有

Project.dll

带有元数据(由 ILSpy 报告)..., Version=1.0.6246.25505, Culture=neutral, PublicKeyToken=null...

所以你得到了很好的 nuget 版本和简单的汇编版本(总是在变化,所以如果包版本保持不变,在构建过程中没有奇怪的缓存,比如本地测试)

Source