Custom Build Steps, Tools, and Events

Custom Build Steps, Tools, and Events

Rate This
  • Comments 15

The VC++ build system is of course MSBuild based in VS 2010.  This automatically gives you a great deal more flexibility to customize your build than you had with .vcproj project files in prior releases.  We went out of our way to ensure that the .targets files that drive the build of a .vcxproj file in VS 2010 leverage as much MSBuild flexibility as possible. 

Some build extensions are very simple: just execute a hard-coded command at a given place and time during the build.  Others are more complex: any time you see a *.crt file run a custom tool on it, setting parameters based on properties on that item, etc.  We wanted to keep the simple scenarios simple, and save the complexity that comes with flexibility for only those scenarios that needed it.  This post will discuss a couple of the simple build extension options you have, and how they’ve changed since VS 2008.

In VS 2008, Visual C++ had a couple of the simple scenarios called Custom Build Step and Custom Build Tool.  Actually these ideas ran together in not so obvious ways in 2008 which made them a bit confusing despite their intention to support the simple scenarios.  So let’s just look at what they are in VS 2010.

Foreword about build extensions

First some overall truths about these build extensions:

  • For each of these build extensions, the command line that is executed is stored in the MSBuild project file as item [definition] metadata, which means you can use any existing project property you want in assembling the command line. 
  • The command gets emitted into a batch file and executed, so your command line is processed as one command per line, allowing multiple commands to be executed.
  • Any command works, even “echo”, since the command is actually passed to cmd.exe for processing.
  • The command executes synchronously, so the rest of the project’s build is held up while your command is executing.
  • If the command you execute returns a failing error code, the entire build fails.
  • The command runs with the VC environment variables and path set (unless you go out of your way to schedule the command to run before the SetEnv target).
  • There is a :VCEnd label emitted at the end of the generated command file, which can be used to skip the rest of your commands with “goto VCEnd”
  • You can see the output of the executed command in the Visual Studio Output window under normal verbosity, and can see the actual command itself with verbosity set to detailed.

Custom Build Step

Custom Build Steps are the quick (but not dirty) way to get an arbitrary command executed at a specific point during the build.  The Custom Build Step is a project level build step (meaning it runs only once during a project build rather than once per file).

The way the Execute After and Execute Before properties work may not be totally obvious.  It hooks into MSBuild’s new “before and after” targets feature, which allows an MSBuild target to declare any arbitrary target included in the project (or its imports) that this target should run before or after.  Important ramifications of this include:

  1. The custom build step only runs when one of the targets you select are run as a normal part of the build.  So if you select “Execute After: CLCompile”, and the CLCompile target doesn’t run because the target’s Condition evaluates to false then will your custom build step won’t run either. Note that if CLCompile’s Condition evaluates to true, but is skipped anyway because MSBuild deems the target is up to date already, its before and after targets execute anyway.
  2. You can list multiple targets as a semi-colon delimited list.
  3. The custom build step runs only once during the build, at the first qualifying opportunity – not before and after every target you listed.  So if you say “Execute Before: CLCompile” and “Execute After: Build”, then during the build, whichever of those happens first will determine when your custom build step runs, and the second qualifying point in the build (if there is one) will not execute the custom build step a second time.
When your Custom Build Step runs (or doesn’t!)

Custom Build Steps are included as part of the intelligence that drives incremental build using the “Additional Dependencies” and “Outputs” properties.  If a file in your “Additional Dependencies” property is touched, then your Custom Build Step will be considered out of date and will build with your next build (as long as the .targets it runs before/after are also run). 

The Outputs property is a semicolon-delimited list of files, and must list whatever files are generated as part of the execution of your custom build step.  If you leave your Outputs property empty, your Custom Build Step will never execute, since MSBuild will determine that no outputs are out of date.  If your step doesn’t generate files but you still want it to execute with every build, making up a fake filename will do the trick, since that file will never exist and MSBuild will always determine that the custom build step is out of date. However, before you do this, consider Build Events (below).

Custom Build Tool

Custom Build Tools allow you to execute a custom command line for an individual source file as part of the build.  Custom Build Tool is a file level build extension, and without a source item being marked CustomBuild, this tool won’t do anything.  You can right-click a CustomBuild project item in Solution Explorer and click Compile to invoke your custom build tool.

Any source item (read: file in your project) can have a custom command line executed during the build by setting its Item Type property in the property pages for that item to “Custom Build Tool” (or CustomBuild in the project file directly).  If using the UI, be sure to click Apply to see the Custom Build Tool property page appear.

In the Custom Build Tool property page, you may notice it looks very similar to the Custom Build Step property page.  This is more by coincidence than design.  But there is a legacy-compatibility link between Custom Build Step and Custom Build Tool in VS2010: custom build tools always run at the time the custom build step would run; except when it doesn’t.  When no value is specified for Execute Before and Execute After for the Custom Build Step, the before and after targets for custom build step and custom build tool vary (go figure) in order to maintain compatibility with VS 2008.  In all other ways these two build extensions are totally unrelated.

Custom Build Tool is targeted at the exceptional source item case (you have just one source item that needs this kind of command executed on it) rather than a group of files.  If you’re trying to set up a command that runs on all .customextension files, you should consider using a Custom Build Rule (now called Build Customization) rather than a Custom Build Tool, as that is exactly what custom build rules are for.

If Custom Build Tool is file-level, why do you see Custom Build Tool in your project level property pages?  Because just like any other tool (i.e. CL or Link), you can set project-level defaults for its properties.  But these properties have no impact on the project itself… only on project source items that are of the CustomBuild type.

Build Events

Nothing much changed here since VS2008.  These three events (Pre-build, Pre-link, and Post-build) fire during the course of a normal build and allow you to execute arbitrary project-level commands.

Leave a Comment
  • Please add 8 and 5 and type the answer here:
  • Post
  • What I'd really like to understand is how to:

    1) write custom MSBuild tasks

    2) run those custom MSBuild tasks as custom build steps, and/or have custom MSBuild tasks kicked off when my solution build finishes

    3) write an MSBuild task that builds my solution with either new or modified preprocessor definitions

    Are there resources for learning how to do things like that?

    Thanks,

    Eric

  • Off-topic, but is anyone of your team (and preferable a program manager) aware of what's happenning on Microsoft Connect about complaints related to VS 2010 Help System?

    https://connect.microsoft.com/VisualStudio/feedback/details/505032/help-is-missing-an-index

    There are hundreds of guys looking forward for some attention.

  • @Grael: Thanks for bringing the help issue to our attention.

    The shell or project teams do not own the Help system, so we were unaware of the Connect posts. Also, it looks like the Connect issue was tracked internally as VS10 bug #809715 which was closed in VS10 RC time frame, and since all the complaints were added to the closed issue it's likely the Help team was also not aware of the negative feedback.

    I've reactivated now the internal bug and the Help team should be able to see it and reply properly to the customers.

    Alin Constantin

    [Visual Studio Shell Development]

  • @small_mountain: You can find some information on how to write a custom MSBuild task on MSDN here: http://msdn.microsoft.com/en-us/library/t9883dzc.aspx.

    For extending the build process, such as running your tasks before or after other parts of the build, you can add new targets to your project files which will trigger at the appropriate time.  Assuming you know how to write a target (just look at any project file, or MSDN has resources for this), you can use BeforeTargets and AfterTargets to extend any aspect of the build process.  See my blog post here:

    http://blogs.msdn.com/msbuild/archive/2010/02/18/build-extensibility-with-net-framework-4.aspx.

  • I have what I believe should be a task that should be easy to accomplish with MSBuild, however I'm since I'm new to it, I'm struggling.

    Specifically, I would like to run a command line tool against each C++ source file in my project prior to the compiler (CLCompile?) task running.  Is this possible?  If so, where should I start?  I started looking at a custom ITask implementation, but this seems like overkill for this.

    Any insight would be greatly appreciated!

  • @BukesRos:  You are right, that is overkill :)  Depending on the nature of your task, it may be as simple as using an Exec task and batching.  For instance, suppose you want to just run the 'echo' command for each project file.  Here's how you would do it (in your target):

    <Exec CommandLine="echo About to compile %(CLCompile.Identity)..."/>

    The % syntax will ensure that the Exec task is run once for each unique value of the Identity metadata on your @(ClCompile) items.  The Identity metadata maps to the include value of your items, which in this case is probably what you want.  If you need the full path, you can use %(CLCompile.FullPath) instead.

    For more info on batching, see the MSDN article here: http://msdn.microsoft.com/en-us/library/ms171474.aspx

  • @Cliff - Thanks for your prompt response!  This looks like it should give me what I am looking for.  

    So, given a C++ project file like this one: http://msdn.microsoft.com/en-us/library/2208a1f2.aspx

    Where would I insert the EXEC item, as this project file doesn't contain an explicit <TARGET>.  My guess, the line

    <Import project="$(VCTargetsPath)\Microsoft.Cpp.targets" />

    is defining the target(s).  Is it possible to extend such targets?

  • @BukesRos, yes you can extend the targets in there in many ways. You can override wholesale targets by name, to completely replace their implementation; you can make a target invoke your target before it or after it. For the latter see the blog post Cliff linked to about BeforeTargets and AfterTargets.

    You wouldn't modify the Microsoft.Cpp.targets file itself. Instead you would put your Target/s after it in the "logical" project. That would mean either simply pasting it in below the Import in the project (if it's just for this project) or pulling it in from your own shared .targets file with an Import below this Import. In the latter case, you might chose to move the existing Import into your Import, so you only have one in the project. Easier to maintain that way.

    To learn how to do stuff with MSbuild, I recommend this book: http://www.amazon.com/Inside-Microsoft-Build-Engine-PRO-Developer/dp/0735626286 ("Inside the Microsoft Build Engine: Using MSBuild and Team Foundation Build"). It will get you up to speed with all the 2.0 and 3.5 features. It doesn't have the 4.0 features yet, nor mention of VC projects; I believe they are working on a new edition to cover these as we speak.

    Dan

  • @BukesRos:  You will have to find a target somewhere, either in your project or in one of the imported projects.  As it is generally considered bad form to alter the standard targets files (such as Microsoft.Cpp.targets), you will want to use one of our extensibility mechanisms such as BeforeTargets instead.  

    First, find out where your files are being compiled.  Probably some target called 'Compile' somewhere leads to this.  You only need to know the name of the target responsible.

    Then, add a target to your project, such as:

    <Target Name="MyPreCompileSteps" BeforeTargets="Compile">

       <Exec CommandLine="echo %{CLCompile.FullName}" />

    </Target>

    Your target MyPreCompileSteps will now run before Compile does, ensuring you have a chance to process your input files.

  • Thanks you all for your help - I've managed to get this working.  For posterity, here is what I ended up with:

    <Target Name="RunMyTask" BeforeTargets="CLCompile">

       <Exec Command="echo MyTask: %(CLInclude.FullPath) />

       <Exec Command="echo MyTask: %(CLCompile.FullPath) />

    </Target>

  • I have vc++2008 express edition.

    I have our own .urf files which will be converted to .c file using our own application (compiled in vc++ aswell).

    Now I created new project, added .urf files and write my own custom build step. When I run the "Build" , it creates the .c files as expected, but these .c files are not getting included in the project hence .obj files from these .c files are not created.

    How to inform vc++ to include these auto generated .c files into project and consider for compilation??

    Thanks in Advance

  • @vc_newbie - A simple way to solve this would be to simply add a .c/.cpp source file to your project that #includes each of your generated .c files.

    For example, if you have

    foo1.urf -> foo1.c/.h

    foo2.urf -> foo2.c/.h

    You'd create urfRoutines.c that would look something like this:

    #include "foo1.c"

    #include "foo2.c"

    Good luck.

  • @BukesRos and @vc_newbie

    >A simple way to solve this would be to simply add a .c/.cpp source file to your project that #includes each of your generated .c files.

    You can, but there's a better (more powerful and maintainable) way. It would work if your build rule was share by many projects on different named files, for example. What you need to do is simply put your items in the list that goes to the compiler. By looking in the c++ .targets files under  %programfiles%\msbuild, you can find the invocations of the <CL> task, and see that Sources is passed the list named @(ClCompile):

       <CL Condition="'%(ClCompile.PrecompiledHeader)' == 'Create' and '%(ClCompile.ExcludedFromBuild)'!='true'"

           BuildingInIDE                      ="$(BuildingInsideVisualStudio)"

           Sources                            ="@(ClCompile)"   <-------------------

    ...

    so your Target needs to put its outputs into there. Looking in your XXX.targets file holding the rule you will see a <Target> named "ComputeYYYOutputs". In there, add something like this

       <ItemGroup>

         <ClCompile Include="%(ZZZZ.Outputs)"      />   <------------------ "add items named for the Outputs metadata on each item in the ZZZZ named list"

       </ItemGroup>

    (ZZZ is the type of item that your rule consumed. I believe it is the same as your rule name -- look higher up and you'll see where the rule task runs:

       <ZZZ

         Condition="'@(ZZZ)' != '' and '%(ZZZ.ExcludedFromBuild)' != 'true'"   <----------------- clearly consumes the list @(ZZZ) and various metadata like %(ZZZ.abc)

         CommandLineTemplate="%(ZZZ.CommandLineTemplate)"

         MyProperty="%(ZZZ.MyProperty)"

         AdditionalOptions="%(ZZZ.AdditionalOptions)"

         Inputs="%(ZZZ.Identity)" />

    from which you can see what ZZZ is the "item type" in your case.)

    In your case, you presumably want to compile as C. That should happen automatically, I think, as the file extension is .c and later in the VC build rules I see it special cases that (not really the MSBuild way!); but if you needed to force it, you'd do something like this instead

       <ItemGroup>

         <ClCompile Include="%(ZZZ.Outputs)" >

            <CompileAs>CompileAsC</CompileAs>   <-------------------

         </ClCompile>

       </ItemGroup>

    Here I'm adding appropriate metadata to tell the build process further on to compile the outputs as C, which it will ultimately pass to CL with a switch. (Unlike in makefiles, in MSBuild you generally need never know about switches, just /what you want to happen/.). The build process recognizes very many different kinds of metadata on CLCompile and other major item lists.

    How did I find 'CompileAs' ? You can set it in the project property pages and see what it writes into the project, ie

     <ItemGroup>

       <ClCompile Include="UUU.cpp">

         <CompileAs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">CompileAsC</CompileAs>   <-------------------

       </ClCompile>

     </ItemGroup>

    So what happened here? You told it whatever the output items were for the rule, which you defined in your CBR's .props file something like this (or the VS2008 rules converter did - we need the UI back!)

     <ItemDefinitionGroup>

       <BAZ>

         <MyProperty>False</MyProperty>

         <CommandLineTemplate>copy [inputs] blah.bazout</CommandLineTemplate>

         <Outputs>%(Filename).bazout</Outputs>   <-----------defines @(BAZ.Outputs) on every BAZ item subsequently created by an <ItemGroup> you see further up in the .targets

         <ExecutionDescription>This does bazzing</ExecutionDescription>

       </BAZ>

     </ItemDefinitionGroup>

    ... those items should go to the CL compiler. To achieve this, note, you didn't need to know their file extensions at the point you modified -- they were put in the "Outputs" metadata on the "Baz" (in this case) items that the Baz build rule consumed. So when you need them, you referred to them as %(Baz.Outputs). (Actually um I used ZZZ above.). The MSBuild concept here is "item lists" and each item can have "metadata" on it; the item lists "flow" through the build, they have global scope within one project's build, possibly being added to or having items removed, or having their items' metadata changed. Note that after the point that the items were created, you didn't need to know WHERE the files were, or WHAT names they had, and you didn't need to know WHERE they subsequently are processed or how (other than that there is later compilation that consumes @(ClCompile) items). Compare Makefiles: they have macros that are a bit like item lists but (1) they aren't first class lists, just strings with separators whose entries are hard to manipulate and (2) the elements in that list don't have metadata certainly not default metadata.

    I hope this long winded explanation gives you an idea of not only how to do this "properly" but one of the MSBuild Ways, which I think is quite powerful. Take a look at the MSPress MSBuild book for details of items and the syntax; you'll have to wait for the 2nd edition later this year to get details on custom build rules and VC build process.

    Dan

    PS Check out ItemDefinitionGroups; these are like declarations of an item list name ("item type"): they don't create any items, but specify default metadata on all of the items subsequently created with ItemGroup. Pretty powerful: take a look at the VC .props files in %programfiles%\msbuild as they use them extensively.

  • Introducing MSBuild into VC++ looks like a real improvement for platforms where it is available.  

    Can you comment on the status of VC++ 'Makefile' projects in VS 2010?

    Thanks,

    David.

  • Hello, I'm Rodel Blair. And I really love to read your blog as well as the examples that you've mention. Thank you so much for this helpful contents. I am looking forward to see more example contents like this.

    If you have time you can also visit this site that I managed to surf in:

    <a href="kcsbuildingproducts.com/">Building Products</a>

Page 1 of 1 (15 items)