kentie.net

Run a process on output files in VS2010

Suppose you've got various projects in your Visual Studio solution you want to run some action on. Maybe you want to run a packer on some executables, an obfuscation tool on configuration files, etc. One approach is to add a post-build step to each project, but that means the same post-build step has to be added to multiple projects and that it's unclear which files will be processed. Post-build steps also have no knowledge of what files they generate and if those might have been deleted. Additionally, the post-build step can't be easily turned on or off, and I've got time consuming actions I only want to run when I build installers.

Another option is to add an empty project and add the post-build step to that. This requires just one post-build step, but can lead to situations where the build step isn't re-run when it should, or is run too many times. It also requires every single file to be processed each time.

The best solution is an utility project with a bogus file for each file that needs to be processed, and a custom build step attached. The custom build step can be set in a property sheet, and only needs to be entered once. If either the input file is changed or the output file has been deleted, the step is re-run for just that file.

  1. 1
    In this example I've got a solution with two executable projects, Project1 and Project2. As a simple action, I want their output files, Project1.exe and Project2.exe to be copied to CopyOfProject1.exe and CopyOfProject1.exe.

  2. 2
    3
    Add an empty project, and set it as an Utility Project for all configurations.

  3. 4
    5
    In the Property Explorer (it's one of the tabs next to the Solution Explorer), click the project and add a new property sheet. Property sheets are a powerful (and sometimes confusing) way of re-using and stacking project properties; I tend to use sheets with the include/lib settings for major libraries, etc.

  4. 6
    7
    8
    Right-click the property sheet (doesn't matter if it's the one under debug or release) and take a look at its properties. Open the 'General' item under 'Custom Build Tools'. Here the command-line for whatever action needs to be taken can be entered:

    • Command Line: The action to be run. Note the use of %(Filename); this is a special macro for custom build steps. Clicking the 'Macros' button will reveal similar macros.
    • Description: This is shown in the build output log whenever the action is run for a file.
    • Outputs: These are the files the action will generate. If one of these has been deleted, the action will be re-run.
    • Additional Dependences: If one of these files is modified, even outside of Visual Studio, the action will be re-run.
    Note how I'm setting the original project outputs as the Additional Dependencies, and the copied ones as the outputs. Of course, all this assumes all projects (and the custom action project) have the same output directory.

  5. 9
    Now it's time to give the project something to run on. First, add references to all the projects whose output files you want to process. This makes sure they're built before the custom action project.

  6. 10
    10b
    Next, add empty text files to the project for each file you want to process. The aforementioned %(Filename) macro in the custom build step will only process everything up to the first extension in the filename; the second extension can be left out (which in my case would result in empty .exe files in the source tree), or changed to something more descriptive.

  7. 11
    12
    7b
    Select all the files and enter their properties, and enable the custom build step. Upon leaving the properties window and returning, the build step from the property sheet will be visible.

Now build the solution, and after Program1 and Program2 are built the custom build steps will run:

3>------ Build started: Project: CustomBuildExample, Configuration: Debug Win32 ------
3>Build started 21-1-2012 19:19:35.
3>InitializeBuildStatus:
3>  Creating "Debug\CustomBuildExample.unsuccessfulbuild" because "AlwaysCreate" was specified.
3>CustomBuild:
3>  Copying Program1.exe
3>          1 file(s) copied.
3>  Copying Program2.exe
3>          1 file(s) copied.
3>FinalizeBuildStatus:
3>  Deleting file "Debug\CustomBuildExample.unsuccessfulbuild".
3>  Touching "Debug\CustomBuildExample.lastbuildstate".
3>
3>Build succeeded.
3>
3>Time Elapsed 00:00:00.05

All this results in the following behavior. If I:

Rebuild the solution
Program1 and Program2 are rebuilt, both are copied.
Delete Program1.exe
Program1 is relinked and copied again. Program 2 is left alone.
Modify Program1.exe's source code
Program1 is rebuilt and copied again. Program 2 is left alone.
Delete CopyOfProgram1.exe
Program1 is copied again. Program 2 is left alone.
Build the CustomBuildExample project while no files have changed
Nothing happens.
Do a 'Rebuild Project Only' of the CustomBuildExample project
The files get copied again.

Download the example solution

Created: Jan 21 2012
Modified: Jan 21 2012