7

I need nuget pack to generate a package version with only 3 digits (we want to do semantic versioning on it) but when I call it on a csproj which has an AssemblyVersion attribute set to "1.0.0", the resulting nupkg file ends with version "1.0.0.0" in it's metadata (and file name). Why doesn't the command line tool honor the amount of digits specified on the AssemblyVersion attribute?

I started this with a call to nuget spec against the csproj file, which generates a stub nuspec file like this (it actually includes more tags with placeholder values, but I've deleted them since we don't need them):

<?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>

With this nuspec file checked in TFS in the same folder as the csproj file, we can now call pack like this:

nuget pack MyProject.csproj

The project's AssemblyInfo.cs file contains a line to set the version explicitly:

[assembly: AssemblyVersion("1.0.0")]

It is all working perfectly fine, except for the fact that the tool is using 4 digits when it retrieves the assembly version. Even Windows shows the version with only 3 digits when I right click the dll on the file explorer and go to details. Why is NuGet using 4 digits? Am I perhaps missing something obvious?

Hardcoding the version in the nuspec is obviously not ideal, because then we would have to maintain the version number in two different places while they are intended to be always the same. I mean, this was supposed to be the idea behind the special placeholder value $version$ there that NuGet itself knows how to extract from the project.

julealgon
  • 7,072
  • 3
  • 32
  • 77

3 Answers3

9

In the Creating and Publishing a Package it is stated that NuGet uses AssemblyVersionAttribute attribute when the token $version$ is used.
Digging into NuGet source code, I've found that it's not as straight forward as one might think.

NuGet uses reflection to get your library's version. AssemblyName.Version to be exact. Since all components of the version must be integers greater than or equal to zero (see AssemblyName.Version), the version presented is 1.0.0.0 (in your case) rather than 1.0.0 as declared in your AssemblyVersion attribute.

Possible solution
The Nuspec Reference page adds more information next to the $version$ token. It mentions AssemblyInformationalVersionAttribute attribute will get precedence over AssemblyVersionAttribute. Using it will solve your problem.

Digging deeper
You might wonder why would AssemblyInformationalVersionAttribute work and AssemblyVersionAttribute won't?

The answer to that question is that NuGet uses CustomAttributeData.GetCustomAttributes(Assembly) function to retrieve the attributes of your library, before it uses Assembly.Version. The above function will not list AssemblyVersionAttribute, yet it will list AssemblyInformationalVersionAttribute if it was used on the assembly. Only then, if AssemblyInformationalVersionAttribute wasn't found, Assembly.Version will be used.

EDIT:
Relevant NuGet source code:
NuGet uses the following code to get the assembly version (if AssemblyInformationalVersionAttribute wasn't found):

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

The problem starts with assembly.GetName() as it creates and initializes AssemblyName with relevant parameters, including the version.
(That code can be found in AssemblyMetadataExtractor class, under public AssemblyMetadata GetMetadata(string path) function.)

Peas
  • 313
  • 1
  • 9
  • That's interesting... I think I can use the `AssemblyInformationalVersion` without too much trouble, even though it's not ideal. I wonder how Windows can show the correct number of digits when I go to the details page of the dll file though. I assume NuGet would be able to use the same logic to extract that. Any word about it? – julealgon Feb 03 '15 at 12:32
  • Also, could you post the relevant parts of the NuGet source that deal with this perhaps? By looking at the [`Version` source](http://referencesource.microsoft.com/#mscorlib/system/version.cs,0c15515dfe051d35) it is very clear to me that it supports 2 and 3 digit versions. You can see it has a bunch of different constructors for that, and that it's `ToString` overload takes into account which fields were specified and which weren't, by checking their values against -1. In my case, `Version.ToString()` would return "1.0.0" as I expected. – julealgon Feb 03 '15 at 12:46
  • Some fun with the mentioned attributes led me to the following - product version property found in the windows details page of a library will show the version found in the assembly attribute, in the following order: (a) `AssemblyInformationalVersion`, (b) `AssemblyFileVersion`, (c) `AssemblyVersion` (meaning (a) takes precedence, then (b), then (c)). What happens when you only specify an `AssemblyVersion` attribute (with "1.0.0" as parameter, for example)? Windows product version will show "1.0.0.0". And I believe that was the missing piece you were looking for. – Peas Feb 03 '15 at 16:46
  • You are right... I thought Windows was returning the correct number of digits, but it seems I did my tests with `AssemblyFileVersion` in the assemblyinfo.cs file by mistake. – julealgon Feb 03 '15 at 17:35
3

Ran into this same problem and solved it using the "-version" attribute of nuget. It overrides the version from the nuspec file. Works wonderfully for semantic versioning.

Based on the above, I'm guessing it wasn't available at the time this discussion was started.

Dave H
  • 31
  • 2
1

NuGet seems to be using [assembly: AssemblyInformationalVersion()] as a version, not AssemblyVersion nor AssemblyFileVersion

Set AssemblyInformationalVersion to 2 or 3 components and nuget will package it exactly as you specified

I'd also set AssemblyVersion to some auto-incremented thing since it's used by .NET and based on my MSBuild experience it's safer to force it to think version changes all the time so it does not try to be smart about it

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

Results in

Project.1.0.nupkg

Containing

Project.dll

with metadata (as reported by ILSpy) ..., Version=1.0.6246.25505, Culture=neutral, PublicKeyToken=null...

So you get nice nuget version and fool proof assembly version (always changing so no weird caching during builds if package version stays the same, say for local testing)

Source

illegal-immigrant
  • 8,089
  • 9
  • 51
  • 84