How to automatically insert version number into As

2019-05-20 04:59发布

问题:

I'm trying to build upon this question:

Reading a single value from a file in MSBuild

My goal is to have a single place to put the version number that's used in several projects, and I also want a portion of the version number in the DLL file name for one of the projects.

Based on the question above, I already got the first part, but I'm having difficulty with the second part and would appreciate some guidance.

In my solution, I set up a plain text file called Version.txt containing my full version number only:

1.1.0.0

In both of my projects, I opened their AssemblyInfo.cs files and removed the AssemblyVersion and AssemblyFileVersion items, then modified both projects to generate them in a separate file as described in the question above.

<ItemGroup>
    <VersionFile Include="..\Version.txt" />
</ItemGroup>
<Target Name="BeforeBuild">
    <ReadLinesFromFile File="@(VersionFile)">
        <Output TaskParameter="Lines" PropertyName="VersionNumber" />
    </ReadLinesFromFile>
    <Delete Files="Properties\Version.cs" />
    <WriteLinesToFile File="Properties\Version.cs" Lines="using System.Reflection%3B&#xD;&#xA;&#xD;&#xA;[assembly: AssemblyVersion("$(VersionNumber)")]&#xD;&#xA;[assembly: AssemblyFileVersion("$(VersionNumber)")]" />
</Target>

Now when I build, I get a generated Properties\Version.cs file for each project, which is used to build the EXE/DLL and shows up as "1.1.0.0" in their file properties. This is exactly what I want.

For the DLL, I would like to name the assembly "filename.v1.1.dll", where the "1.1" comes from the first two components in Version.txt above. I'm flexible on the format of Version.txt as long as I can get the full "1.1.0.0" in the EXE/DLL properties and "1.1" in the DLL file name.

To try this out, I modified the DLL's csproj file to have:

<RootNamespace>dllfile</RootNamespace>
<AssemblyName>dllfile.v$(VersionNumber)</AssemblyName>

Of course, this will insert the full version number in the file name, which I don't want.

Does anyone have any tips on how to proceed?

Thanks.

EDIT: I have been able to extract the major/minor components of the version number by adding the following to my .csproj BeforeBuild target:

<ReadLinesFromFile File="@(VersionFile)">
    <Output TaskParameter="Lines" PropertyName="VersionNumber" />
</ReadLinesFromFile>
<PropertyGroup>
    <VersionNumberFirstDotIndex>$(VersionNumber.IndexOf('.'))</VersionNumberFirstDotIndex>
    <VersionNumberMajorStart>0</VersionNumberMajorStart>
    <VersionNumberMajorLen>$(VersionNumberFirstDotIndex)</VersionNumberMajorLen>
    <VersionNumberMinorStart>$([MsBuild]::Add(1, $(VersionNumberFirstDotIndex)))</VersionNumberMinorStart>
    <VersionNumberSecondDotIndex>$(VersionNumber.IndexOf('.', $(VersionNumberMinorStart)))</VersionNumberSecondDotIndex>
    <VersionNumberMinorLen>$([MSBuild]::Subtract($([MSBuild]::Subtract($(VersionNumberSecondDotIndex), $(VersionNumberFirstDotIndex))), 1))</VersionNumberMinorLen>
    <VersionNumberMajor>$(VersionNumber.Substring($(VersionNumberMajorStart), $(VersionNumberMajorLen)))</VersionNumberMajor>
    <VersionNumberMinor>$(VersionNumber.Substring($(VersionNumberMinorStart), $(VersionNumberMinorLen)))</VersionNumberMinor>
    <VersionNumberShort>$(VersionNumberMajor).$(VersionNumberMinor)</VersionNumberShort>
</PropertyGroup>
<Message Text="DEBUG1 VersionNumberFull=$(VersionNumber)" Importance="High" />
<Message Text="DEBUG2 VersionNumberAbbrev=$(VersionNumberShort)" Importance="High" />
<Delete Files="Properties\Version.cs" />
<WriteLinesToFile File="Properties\Version.cs" Lines="using System.Reflection%3B&#xD;&#xA;&#xD;&#xA;[assembly: AssemblyVersion(&quot;$(VersionNumber)&quot;)]&#xD;&#xA;[assembly: AssemblyFileVersion(&quot;$(VersionNumber)&quot;)]" />

The only piece I'm missing now is how to get this VersionNumberShort into the DLL file name. Unless someone has a better idea, I can take Peter's suggestion and use Move tasks:

<Target Name="AfterBuild">
    <Move SourceFiles="$(OutputPath)$(AssemblyName).pdb" DestinationFiles="$(OutputPath)$(AssemblyName).v$(VersionNumberShort).pdb" />
    <Move SourceFiles="$(OutputPath)$(AssemblyName).dll" DestinationFiles="$(OutputPath)$(AssemblyName).v$(VersionNumberShort).dll" />
</Target>
<Target Name="AfterClean" DependsOnTargets="Common">
    <Delete Files="$(OutputPath)$(AssemblyName).v$(VersionNumberShort).pdb" ContinueOnError="true" />
    <Delete Files="$(OutputPath)$(AssemblyName).v$(VersionNumberShort).dll" ContinueOnError="true" />
</Target>

Since I needed the same property definitions as before, I moved the snippet above into a "Common" target and referenced it in both the build and clean tasks shown here.

Peter - If you want to move your comment as an answer, I'll accept it.

Thanks!

EDIT: Following jdlugosz's answer, I tried setting the AssemblyName inside my task. Unfortunately, this still didn't seem to have any effect based on the original example listed at the top:

<Target Name="BeforeBuild">
    ...
    <WriteLinesToFile ... />
    <PropertyGroup>
      <AssemblyName>dllfile.v$(VersionNumber)</AssemblyName>
    </PropertyGroup>
</Target>

I tried running this with MSBuild from a Visual Studio Developer Command Prompt:

msbuild /target:clean projfile.csproj
msbuild /verbosity:diag projfile.csproj > out.txt

Prior to this, I renamed the at the top of my csproj file and in the "redefinition" to something unique to make it easy to search (e.g. "dllfileoriginal" vs. "dllfilemodified").

Looking through the output log, I can't find any reference to the modified text; it's still dllfileoriginal everywhere in the output.

Following the WriteLinesToFile task, it looks like the following targets were built:

  • IncrementalClean (finished)
  • PostBuildEvent
  • CoreBuild
  • AfterBuild
  • Build

There's no reference to either DLL name inside these.

It looks like the is currently my best bet still.

回答1:

The Target Name is is shown on the General page under the Configuration Properties tab in the IDE Property Page editor. I don't have one handy myself to look up the name for you, but you can do it by changing the blank in the IDE to something like XXXX and save. Then view the diff in the version control commit reviewer and see what the name of the Property is. In this case, then edit the line to change XXXX to $(OutputPath)$(AssemblyName).v$(VersionNumberShort)

Oh, check out the FormatVersion task, which might help. I think there are some premade tasks that manipulate a version assembly similar to what you show, too.

What I'm doing for versions is passing the pieces in via #defines as /D command line arguments. I guess you don't have that in C# though, IIRC.



回答2:

This works for me, and it solves the seemingly simple problem of appending the version info string to a filename at build.

First, the post-build event:

(Right-Click Project -> Properties -> Build Events -> Edit Post-build...)

$(TargetPath) "version" > $(TargetDir)text.txt
set /p version= <$(TargetDir)text.txt
copy $(TargetPath) $(TargetDir)$(TargetName)_%version%.exe
del $(TargetDir)text.txt

Now, the trick: Overload sub main to return the version info, and call it in a post-build event on the exe that was just built.

here is an example in F#:

[<EntryPoint>]
let main argv = 

    let version = argv.Length = 1 && argv.[0] = "version"
    if version then
        let version = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString()
        do stdout.WriteLine(version)
        do stdout.Flush()
    else 
        try
        //...

The post-build event above

1) calls the newly built exe with a "version" arg, and writes the output to a txt file

2) reads the text file contents into a local variable

3) renames the newly built exe by adding the version info

3) copies the newly built exe adding the version info to the name

4) cleans up the temp file

*changed "move" to "copy" so that Visual Studio can still F5 the project