This project has moved. For the latest updates, please go here.

Suggestion: Use AfterBuild target in msbuild instead of the post-build project event

Jul 2, 2014 at 10:18 PM
I'll use this discussion to point out two things I've done in one of my projects. The snippet below is from https://github.com/alexdresko/Konami/blob/master/Konami.Core/Konami.Core.csproj.

Notice the AfterBuild target. That's how I moved the post-build event to my .csproj msbuild file. Note also that my BuildNewPackage-RanAutomatically.ps1 file (in the same github repo) now accepts a version number which is created in the BeforeBuild target using the MSBuild Community Tasks. This technique also allows the nuspec file to use a semver-style version number (Major.Minor.Build).

Thoughts?
   <Target Name="BeforeBuild">
      <Message Text="Setting the version" />
      <Version BuildType="Increment" RevisionType="None" Major="1" Minor="0" VersionFile="VersionInfo.txt">
         <Output TaskParameter="Major" PropertyName="Major" />
         <Output TaskParameter="Minor" PropertyName="Minor" />
         <Output TaskParameter="Build" PropertyName="Build" />
         <Output TaskParameter="Revision" PropertyName="Revision" />
      </Version>
      <Message Text="Version: $(Major).$(Minor).$(Build).$(Revision)" />
      <AssemblyInfo CodeLanguage="CS" OutputFile="$(MSBuildProjectDirectory)\VersionInfo.cs" AssemblyVersion="$(Major).$(Minor).$(Build)" AssemblyFileVersion="$(Major).  $(Minor).$(Build)" />
   </Target>
   <Target Name="AfterBuild">
      <Exec Command="PowerShell -NoProfile -ExecutionPolicy Bypass -Command &quot;&amp; '$(ProjectDir)PostBuildScripts\BuildNewPackage-RanAutomatically.ps1' -ProjectFilePath   '$(ProjectPath)' -OutputDirectory '$(TargetDir)' -Configuration '$(ConfigurationName)' -VersionNumber '$(Major).$(Minor).$(Build)' -Platform '$(PlatformName)'&quot;" />
   </Target>
Coordinator
Jul 3, 2014 at 12:28 AM
Interesting idea; I never thought of doing it using build targets. I'm curious though, what benefit does calling the BuildNewPackage-RanAutomatically.ps1 script this way gets us? If you want the version number to always be in the format Major.Minor.Build, that's easy enough to do by adding a line to the BuildNewPackage-RanAutomatically.ps1 script.
Jul 3, 2014 at 1:01 AM
Edited Jul 3, 2014 at 1:05 AM
There's a lot more power in build targets than the After-build event in VS. The next thing I plan on doing is creating a build task that fetches the latest commits and closed issues for my project from github since the last successful push to nuget, then set that as the release notes in the nuget package.

And I can do things like append "-prelease" to the version depending on the build configuration.

I could probably think of more things if you're not convinced yet. :)
Jul 3, 2014 at 1:07 AM
For that matter, I could create a build configuration that automatically initiates the push to nuget. And, given that it's all done in MSBUILD, it should work just fine from an automated CI build.
Jul 21, 2014 at 4:16 PM
I agree with alexdresko.
Another added bonus is Conditionals could be included in the AfterBuild easily. A development build doesn't need to create the nuget package typically, but a Release build would, therefore could only execute the target with a Release Configuration, with very little if any manual changes to the proj file after installing your package.
Coordinator
Jul 21, 2014 at 5:14 PM
I'm still considering this as an option. I know a bit about MSBuild targets, but haven't written any of my own yet. Almost every developer I've spoke with is more familiar with Batch and PowerShell files then they are with MSBuild targets. We have over 100 developers where I work, and many of them are scared to directly edit a .csproj file; they are much more comfortable editing a batch or PowerShell file. By using the Post-Build event, developers can easily see the text directly in Visual Studio, and feel safer about changing it if they need. If they mess it up, the project will still open in Visual Studio, where if you make a syntax problem editing the build targets, your project may not open in VS anymore and just give a generic error. Also, it doesn't require developers to go and learn a new "language" (msbuild targets); they just see batch/PowerShell commands that they already know.

@cadditon You can easily do conditionals in the Post-Build event as well. To do the scenario you described of only building a package in Release mode, you simply need to add an If statement to the Post-Build event, so change it from:
REM Create a NuGet package for this project and place the .nupkg file in the project's output directory.
REM If you see this in Visual Studio's Error List window, check the Output window's Build tab for the actual error.
ECHO Creating NuGet package in Post-Build event...
PowerShell -NoProfile -ExecutionPolicy Bypass -Command "& '$(ProjectDir)_CreateNewNuGetPackage\DoNotModify\CreateNuGetPackage.ps1' -ProjectFilePath '$(ProjectPath)' -OutputDirectory '$(TargetDir)' -BuildConfiguration '$(ConfigurationName)' -BuildPlatform '$(PlatformName)'"
to:
If $(ConfigurationName) == Release (
REM Create a NuGet package for this project and place the .nupkg file in the project's output directory.
REM If you see this in Visual Studio's Error List window, check the Output window's Build tab for the actual error.
ECHO Creating NuGet package in Post-Build event...
PowerShell -NoProfile -ExecutionPolicy Bypass -Command "& '$(ProjectDir)_CreateNewNuGetPackage\DoNotModify\CreateNuGetPackage.ps1' -ProjectFilePath '$(ProjectPath)' -OutputDirectory '$(TargetDir)' -BuildConfiguration '$(ConfigurationName)' -BuildPlatform '$(PlatformName)'"
)
That's pretty simple. For other more complicated scenarios like what @alexdresko describes, you can easily create a PowerShell file to grab the latest commits and set the release notes and whatnot, and then just add one more line to the Post-Build event code to call that PowerShell script.

The only real benefit I can see from using build targets is that you don't require any external files; everything gets put directly into the .csproj file. But as I mentioned, I think most developers are much more comfortable modifying a .bat or .ps1 file then they are a .csproj, so using build targets may be making things needlessly more complicated/advanced.

I feel that one option doesn't really provide more value over the other; it's just using two different technologies to accomplish the same task.

Thoughts?
Aug 22, 2014 at 4:13 PM
Although I agree that developers editing the project file is undesirable, I disagree that MSBuild is too complicated. It allows for greater flexibility and easier unit testing of your development to support package deployment (this becomes more important as the complexity of the development grows). It also prevents the possibility of an unsuspecting developer from breaking deployment by editing their after build events, which is exposed by the project properties.

Also, if the install and uninstall scripts handle the addition and removal of the project MSBuild xml tags, then no developer would need to handle that.
As for configuration, you can use a separate file for that like NuGet does.

Since we are on the topic. I would also recommend removing the need to install all the scripts inside of a project. This is ugly at best and pollutes the project. If you have a lot of projects, this is a tremendous unnecessary duplication. You should reference the scripts and libraries from the NuGet package folder since that would make the projects cleaner and nobody likes to check in binaries and outside libraries that isn't part of their development.