4

I have a custom .targets file that I use to support post-compile code generation. I override a handful of targets, with the intention being to have the AfterBuild task run the code generator, call CoreBuild again, then tail-recurse until the result converges to a fixed-point or too many iterations are required. (I don't think I've ever seen it need more than 3 compiles to converge, but I use 5 as a limit.)

It's a clumsy hack in some ways, especially by using CycleDepth both to break endless loops and to prevent MSBuild from skipping a repeated task because the properties haven't changed, but it works well for its primary intended use case: generating code to implement object serialization methods and merging them in via the partial classes mechanism, as an optimization to avoid reflection-based serialization at run time. I've run across one situation where it breaks, though: if MSBuild is invoked against the solution with the /m (parallel) option, the build dies horribly: it looks to me as though what are supposed to be iterations are getting invoked concurrently.

Is there any way to implement this sort of recursion without breaking parallel MSBuild?

I am somewhat revision-locked to 3.5 due to a need to target both the standard and Compact Framework; the optimization is necessary to achieve sufficient speed on slow Compact Framework devices.

  <Target Name="BeforeCompile" Condition="Exists('$(ProjectDir)CodeGenerationPostBuild.xml')">
    <PropertyGroup>
      <AssemblyTimestampBeforeCompile>%(IntermediateAssembly.ModifiedTime)</AssemblyTimestampBeforeCompile>
    </PropertyGroup>
  </Target>

  <Target Name="AfterCompile" Condition="Exists('$(ProjectDir)CodeGenerationPostBuild.xml')">
    <PropertyGroup>
      <AssemblyTimestampAfterCompile>%(IntermediateAssembly.ModifiedTime)</AssemblyTimestampAfterCompile>
    </PropertyGroup>
  </Target>

  <Target Name="AfterBuild" Condition="Exists('$(ProjectDir)CodeGenerationPostBuild.xml') and ('$(AssemblyTimestampBeforeCompile)'!='$(AssemblyTimestampAfterCompile)')">
    <Exec WorkingDirectory="$(ProjectDir)" Command="&quot;$(SolutionDir)..\CodeGen\CodeGeneration.Engine\bin\$(ConfigurationName)\CodeGeneration.Engine.exe&quot; &quot;$(ProjectDir)CodeGenerationPostBuild.xml&quot;  Command=Build ConfigurationName=$(ConfigurationName)" IgnoreExitCode="true">
      <Output TaskParameter="ExitCode" PropertyName="PostBuildExitCode" />
    </Exec>
    <Error Condition="$(PostBuildExitCode) == 2" Code="BS0002" Text="Read-only generated code file found; did it get checked in again?" />
    <Error Condition="$(PostBuildExitCode) == 99" Code="BS0099" Text="Error in code generation; see detailed build output." />
    <Error Condition="'$(CycleDepth)' == 'xxxxx' And $(PostBuildExitCode) != 0" Code="BS1000" Text="Compilation taking too many iterations to converge.  Do you have [AssemblyVersion(&quot;1.0.*&quot;)] in your AssemblyInfo.cs?" />
    <MSBuild Condition="'$(CycleDepth)' != 'xxxxx' And $(PostBuildExitCode) == 1" Projects="$(ProjectPath)" Targets="CoreBuild; AfterBuild" Properties="CycleDepth=$(CycleDepth)x" />
  </Target>
Jeffrey Hantin
  • 35,734
  • 7
  • 75
  • 94
  • Were you able to accomplish this? I believe this could be used to what I am trying to accomplish in ony of my projects. – Dryadwoods Apr 03 '17 at 09:11

0 Answers0