Visual Studio Pre/Post Build Macros

Visual Studio allows the user to add pre-build and post-build steps when a project is built. This allows you to add custom steps to prepare the environment, copy files to installer folders, etc. By using previously defined macros, you can get build specific information. Below is an example of some. These are actually MSBuild macros and many more exist – Google it.

MacroValue
ConfigurationNameDebug
OutDirbin\Debug\
PlatformNameAny CPU
ProjectDirC:\dev\ToolSuite\ConsoleApp\
ProjectExt.csproj
ProjectFileNameConsoleApp.csproj
ProjectNameConsoleApp
ProjectPathC:\dev\ToolSuite\ConsoleApp\ConsoleApp.csproj
SolutionDirC:\dev\ToolSuite\
SolutionExt.sln
SolutionFileNameToolSuite.sln
SolutionNameToolSuite
SolutionPathC:\dev\ToolSuite\ToolSuite.sln
TargetDirC:\dev\ToolSuite\ConsoleApp\bin\Debug\
TargetExt.exe
TargetFileNameConsoleApp.exe
TargetNameConsoleApp
TargetPathC:\dev\ToolSuite\ConsoleApp\bin\Debug\ConsoleApp.exe

Be aware that when the pre/post build steps run, the current directory is ProjectDir folder.

Using Robocopy

I like to use Robocopy to move files around in a post-build step – for example, to create installers. The only problem is that Robocopy returns a “1” if files are copied. If Visual Studio sees anything other than “0” as an exit code, it will throw an error. The code below will copy some files and if Robocopy exits with a code of “1”, it will pass Visual Studio an exit code of “0”.

if $(ConfigurationName) == Release (robocopy "$(TargetDir)." "$(SolutionDir)Installer\$(ProjectName)" /s /purge /xf appsettings.json /nfl /njh /njs /ns)

rem Robocopy return values - 0: no files copied, 1: files copied, etc.
echo Robocopy return code: %ERRORLEVEL%
if %ERRORLEVEL% GEQ 8 goto failed

rem Files were copied successfully.
exit 0

:failed
rem Return Robocopy failure code.
exit %ERRORLEVEL%

The Robocopy command requires some explaining…

  • "$(TargetDir)." – Robocopy cannot handle a source (or destination) directory path ending in a backslash. One way to get around this is to add a period to the end of the path. This will transform $(TargetDir) to "C:\dev\ToolSuite\ConsoleApp\bin\Debug\.".
  • "$(SolutionDir)Installer\$(ProjectName)" – Destination directory will be created if necessary. Unlike the source path, we don’t need to do the period trick since $(ProjectName) will not end in a backslash ("ConsoleApp").
  • /s – Copies subdirectories. This option automatically excludes empty directories.
  • /purge – Deletes destination files and directories that no longer exist in the source.
  • /xf appsettings.json – Excludes files that match the specified names or paths. Wildcard characters (* and ?) are supported. If you have more than one, just type a space then the next filename.
  • /nfl /njh /njs /ns – Don’t print unnecessary information to build log.