views:

33

answers:

1

There are plenty of guides out there which help you mimic VS2008's "Custom Build Step" in VS2010 with MSBuild. However, I'd like my build to be smarter and make use of MSBuild. I've written a little MSBuild task which invokes the ANTLR parser generator. That build task works flawlessly when I run it in a simple test MSBuild file. However, when I try to add my task to a C++ project, I run into problems. Essentially I've added this to the top of my project file (Right after the <project> element):

  <UsingTask TaskName="ANTLR.MSBuild.AntlrGrammar"
        AssemblyName = "ANTLR.MSBuild, Version=1.0.0.0, Culture=neutral, PublicKeyToken=d50cc80512acc876" />
  <Target Name="BeforeBuild"
    Inputs="ConfigurationParser.g"
    Outputs="ConfigurationParserParser.h;ConfigurationParserParser.cpp;ConfigurationParserLexer.h;ConfigurationParserLexer.cpp">
    <AntlrGrammar
      AntlrLocation="$(MSBuildProjectDirectory)Antlr.jar"
      Grammar="ConfigurationParser.g"
      RenameToCpp="true" />
  </Target>

However, my target is not being called before build.

How can I add my task to a C++ build?

+1  A: 

Before reading this answer, you'll probably want to see:

The old way of extending MSBuild, and the one mentioned by the reference book I have, essentially is based on overriding default-empty targets supplied by Microsoft. The new way, as specified in the second link above, is to define your own arbitrary target, and use the "BeforeTargets" and "AfterTargets" properties to force your target to run before or after your intended target.

In my specific case, I needed the ANTLR Grammars task to run before the CLCompile target, which actually builds the C++ files, because the ANTLR Grammars task builds .cpp files. Therefore, the XML looks like this:

<Project ...
  <!-- Other things put in by VS2010 ... this is the bottom of the file -->
  <UsingTask TaskName="ANTLR.MSBuild.AntlrGrammar"
  AssemblyName = "ANTLR.MSBuild, Version=1.0.0.0, Culture=neutral, PublicKeyToken=d50cc80512acc876" />
    <Target Name="AntlrGrammars"
      Inputs="Configuration.g"
      Outputs="ConfigurationParser.h;ConfigurationParser.cpp;ConfigurationLexer.h;ConfigurationLexer.cpp"
      BeforeTargets="ClCompile">
      <AntlrGrammar
        AntlrLocation="$(MSBuildProjectDirectory)\Antlr.jar"
        Grammar="Configuration.g"
        RenameToCpp="true" />
    </Target>
  <ImportGroup Label="ExtensionTargets">
  </ImportGroup>
  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
</Project>

As for why this is superior to a PreBuildEvent and/or PostBuildEvent; this is smart enough to not rebuild the .cpps when the grammar itself is not updated. You'll get something like:

1>AntlrGrammars:
1>Skipping target "AntlrGrammars" because all output files are up-to-date with respect to the input files.
1>ClCompile:
1>  All outputs are up-to-date.
1>  All outputs are up-to-date.

This also silences Visual Studio's incessant complaining every time you run the program that it needs to rebuild things, like it does with plain pre- and post- build steps.

Hope this helps someone -- took me frickin forever to figure out.

Billy ONeal