tags:

views:

85

answers:

3

I have an MSBuild file that manipulates the AssemblyInfo file before the application is compiled. At the end of the build, it restores the AssemblyInfo file. It does this by backing up the file, manipulating it, and then after build time, restoring the file.

This works fairly well except when an error occurs during the build. It then does not restore the original file. Is there a way I can tell MSBuild to execute a target at the end of a build no matter if it succeeded or failed?

A: 

I never used it, but from the documentation it seems that the OnError Element is useful to what you're trying to achieve.

Causes one or more targets to execute, if the ContinueOnError attribute is false for a failed task.

João Angelo
I'm using the OnError element for my custom targets/tasks already. This works fabulously. The problem is when the error occurs outside a target that is under my control. So I'd like a super catch all that works as an OnError for any task.
Jason Thompson
@Jason, in that case I'm sorry to say that I can't provide any further help.
João Angelo
+2  A: 

Based on your last comment to the original question I would take another approach, and forget the approach you are currently taking. You should know that your version info doesn't have to be in the AssemblyInfo.cs file. It can be in any code file, just as long as you only have attributes AssemblyVersion and AssemblyFileVersion defined once each. With that being said what I would do is follow these steps:

  1. Remove AssemblyVersion & AssemblyFileVersion from AssemblyInfo.cs
  2. Create a new file, name it whatever you want want in my case I put it at Properties\VersionInfo.cs. Do not add this file to the project.
  3. Edit the project file to include that file into the list of file to be compiled only when you want it

Let's expand a bit on #3. When you build a .NET project, the project itself is an MSBuild file. Inside that file you will find an item declared Compile. This is the list of files that will be sent to the compiler to be compiled. You can dynamically include/exclude files from that list. In you case you want to include the VersionInfo.cs file only if you are building on the build server (or whatever other condition you define). For this example I defined that condition to be if the project was building in Release mode. So for Release mode VersionInfo.cs would be sent to the compiler, and for other builds not. Here are the contents of VersionInfo.cs

VersionInfo.cs

[assembly: System.Reflection.AssemblyVersion("1.2.3.4")]
[assembly: System.Reflection.AssemblyFileVersion("1.2.3.4")]

In order to hook this into the build process you have to edit the project file. In that file you will find an element (maybe more than 1 depending on project type). You should add a target similar to the following there.

<Target Name="BeforeCompile">
  <ItemGroup Condition=" '$(Configuration)'=='Release' ">
    <Compile Include="Properties\VersionInfo.cs" />
  </ItemGroup>
</Target>

Here what I've done here is to define a target, BeforeCompile, which is a well-known target that you can override. See this MSDN article about other similar targets. Basically this is a target which will always be called before the compiler is invoked. In this target I add the VersionInfo.cs to the Compile item only if the Configuration property is set to release. You could define that property to be whatever you wanted. For instance if you have TFS as your build server then it could be,

<Target Name="BeforeCompile">
  <ItemGroup Condition=" '$(TeamFoundationServerUrl)'!='' ">
    <Compile Include="Properties\VersionInfo.cs" />
  </ItemGroup>
</Target>

Because we know that TeamFoundationServerUrl is only defined when building through TFS.

If you are building form the command line then something like this

<Target Name="BeforeCompile">
  <ItemGroup Condition=" '$(IncludeVersionInfo)'=='true' ">
    <Compile Include="Properties\VersionInfo.cs" />
  </ItemGroup>
</Target>

And when you build the project just do msbuild.exe YourProject.proj /p:IncludeVersion=true. Note: this will not work when building a solution.

Sayed Ibrahim Hashimi
Interesting, but I'm not sure how this solves the issue. Let me expand on what I'm doing. When I wish to send a release to test, I create a branch in subversion that specifies the version number. My custom MSBuild task then reads in what branch the working copy is in (as well as other information such as revision number, etc...). It then modifies assemblyinfo.cs to embed such information. On failure, it restores the original version of the file. The reason for this is so that my regex's "know" where to modify the information.
Jason Thompson
More importantly, I don't create an endless loop of uncommitted changes this way. If I commit version 1.0.0.0, I don't have a changed file due to simply building the project. Instead, the file is only changed when I need it to be (during build). I don't want my developers to have to remember to change this information by hand. The idea is that I can look at any file and know exactly where it came from and how it was built. So if someone releases junk on the prod server, I know who to blame and exactly which version of the code I need to modify.
Jason Thompson
@Sayed, +1 I like the solution. too bad cruisecontrol doesn't auto-define a property like this.
Maslow
+2  A: 

What about changing the problem:

  • Add a "template" AssemblyInfo.cs.template to version control that represents your "ideal" AssemblyInfo.cs with regex hooks in there
  • Before build, copy the template to the real and apply your regexes
  • Add some kind of subversion ignore for AssemblyInfo.cs (I'm no svn expert, but I'm pretty sure there is a way you can tell it to ignore certain files)
  • In the event that your devs need to add some kind of customization that would normally appear in an AssemblyInfo.cs (eg InternalsVisibleTo), then get them to add it to a different .cs file that IS checked in.

As a further refinement, combine Sayed's solution with mine and remove version info stuff from the actual AssemblyInfo.cs and have a VersionInfo.cs.template that is checked in, that creates a VersionInfo.cs in BeforeBuild.

Peter McEvoy
Interesting idea... I'll need to think on that, but I think that may be closer to the kind of solution that would work. If it succeeds, then it compiles in the "template file". If it fails, the template file stays in there, but the file I want to protect isn't changed.
Jason Thompson
I'd be interested to hear what you do come up with in the end. Good luck!
Peter McEvoy