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:
- 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.
- You can list multiple targets as a semi-colon delimited list.
- 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.
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.