views:

19

answers:

1

I am trying to set up a proper way to do automatic versioning for my C# projects in .Net.

The requirements are:

  1. the assembly is to be built only when is is not up to date. (normal behaviour in Visual Studio and via MSBuild)
  2. When it is built the assembly is to get the proper version assigned.

I have found a solution to give me the version-strings I want. However, to make the assemblies reflect this version after build I need to change the assemblyInfo.cs file just before it is being built. I ended up enabling this functionality via adding a script to be run on the pre-build event. According to Microsoft documentation this should be fine:

Pre-build events do not run if the project is up to date and no build is triggered.

(ref: http://msdn.microsoft.com/en-us/library/42x5kfw4.aspx):

This is so not true! Pre-build event scripts always execute - even if the project is up to date! That is. The pre-build script is run, even when it doesn't recompile (since it is up-to-date). Since my script updates the assemblyInfo.cs file the project is no longer up-to-date so it recompiles (due to a bug/designflaw I had to add the directive to disable caching). This means that the project recompiles every time, which is of course not what I want - considering that I have 103 projects in my solution...

I have investigated this further by adding both BeforeBuild and BeforeCompile targets in the project-file. Guess what: They also run every time even if the project is up-to-date. I have tried running the build from both Visual Studio and from MSBuild with the same sad results.

Does anybody have a suggestion on how to have scripts run *only when the project is not up to date?

BTW: Before it is suggested: I have reviewed most solutions available on the net, including automatic increments via the "1.*" specifier in assemblyInfo.cs. This is not what I want. My versioning needs are somewhat more complex.

A: 

It doesn't seem anyone had a solution on this, but with extra investigation I was finally able to find a workaround.

After investigating the Microsoft.CSharp.targets file I found that there are no "hooks" in CoreCompile that call targets only when CoreCompile is run. CoreCompile itself depends on comparison of Inputs and Outputs to determine whether it should run or not. Armed with that information I copied the input and output specifications from CoreCompile and voila :)

My custom BeforeCompile task now like this:

<Target
  Name="BeforeCompile"
  Inputs="$(MSBuildAllProjects);
          @(Compile);                               
          @(_CoreCompileResourceInputs);
          $(ApplicationIcon);
          $(AssemblyOriginatorKeyFile);
          @(ReferencePath);
          @(CompiledLicenseFile);
          @(EmbeddedDocumentation); 
          $(Win32Resource);
          $(Win32Manifest);
          @(CustomAdditionalCompileInputs)"
  Outputs="@(DocFileItem);
           @(IntermediateAssembly);
           @(_DebugSymbolsIntermediatePath);                 
           $(NonExistentFile);
           @(CustomAdditionalCompileOutputs)">
  <Message Text="BeforeCompile (only when project is not up-to-date)"/>
</Target>

It looks a bit kludgy, but it works. The inputs and outputs are probably also fairly safe to copy. Can't see any good reason for Microsoft to change them often ...

Spiralis