Visual Studio ProTip: Copying Binaries on Pre and Post-Build Macros
Last year I had to spend a fair amount of time working on C and C++ projects in Visual Studio 2013, and one of the tasks that I had to learn how to do was use Visual Studio’s pre-build and post-build events to copy all of my dependent DLLs into the final output folder for my applications.
In C# we take the ability to do this for granted - if we need to add a reference to another project in our Visual Studio solution we just Add Reference
–> Projects
–> [Project Name]
and boom, we’re all set. Visual Studio will automatically copy our dependent binaries into our final /bin/[Release|Debug]
folder automatically.
In C/C++ - hell no. You have to do this all yourself!
And as it turns out, there’s lots of cases where you might need to do this yourself inside C# projects too. If your application uses run-time loading (Assembly.Load
) or if you need to be able to call Process.Start
to launch a custom application that you’ve written, then you might want to be able to easily debug your application by copying the dependent, non-reference binaries you need into your application’s bin
folder.
I’m going to show you how to do that!
Using Pre-Build and Post-Build Visual Studio Macros
During the course of some routine work on Akka.NET today, I needed a way to test our MultiNodeTestRunner.exe
- a custom unit test runner we developed to help test Akka.NET’s highly availability modules and user-defined, distributed applications built on top of Akka.NET.
This test runner needs to be able to do both assembly run-time loading AND launch third-party processes, so it’s a perfect example of a real-world application that can utilize pre and post-build macros for easy debugging.
So in Visual Studio, I have the following two projects inside the Akka.NET solution:
- Akka.MultiNodeTestRunner - a project that defines the
MultiNodeTestRunner.exe
, used to run allMultiNodeSpec
s. - Akka.Cluster.Tests.MultiNode - a .DLL that contains a bunch of
MultiNodeSpec
tests that will be run by theMultiNodeTestRunner.exe
.
We’re going to copy the output binaries from Akka.Cluster.Tests.MultiNode INTO the bin folder for Akka.MultiNodeTestRunner, so we can run all of the tests using the same working directory as the MultiNodeTestRunner.exe
itself.
Adding a Pre-Build Macro
To add a pre-build macro for a Visual Studio project, right click on the project (in this case,the Akka.MultiNodeTestRunner project) and view Properties, then click on Build Events.
Next, we’re going to use some good-ole MS-DOS commands to XCOPY all of the binaries from Akka.Cluster.Tests.MultiNode into the bin directory of Akka.MultiNodeTestRunner. And we’re using MS-DOS commands because PowerShell is for script kiddies, right! Actually… I have no idea if Visual Studio supports PowerShell :p
echo PREBUILDSTEP for $(ProjectName)
echo Copying files from $(SolutionDir)core\Akka.Cluster.Tests.MultiNode\$(OutDir) to $(ProjectDir)bin\$(Configuration)\Akka.Cluster
if not exist "$(ProjectDir)bin\$(Configuration)\Akka.Cluster" mkdir "$(ProjectDir)bin\$(Configuration)\Akka.Cluster"
xcopy "$(SolutionDir)core\Akka.Cluster.Tests.MultiNode\$(OutDir)*.dll" "$(ProjectDir)bin\$(Configuration)\Akka.Cluster" /i /d /y
if errorlevel 1 goto BuildEventFailed
if errorlevel 1 goto BuildEventFailed
REM Exit properly because the build will not fail
REM unless the final step exits with an error code
goto BuildEventOK
:BuildEventFailed
echo PREBUILDSTEP for $(ProjectName) FAILED
exit 1
:BuildEventOK
echo PREBUILDSTEP for $(ProjectName) COMPLETED OK
This batch file uses XCOPY with wild-card matching to copy all binaries from Akka.Cluster.Tests.MultiNode\bin[Build Configuration] to Akka.MultiNodeTestRunner\bin[Build Configuration]\Akka.Cluster, and we need to paste this into the pre-build event under Debug configuration to make sure that this runs.
Now we just need to kick off a build, and we should see all of these echo
statements log their output into the Output window in Visual Studio.
All of our output was successfully copied, and we can verify that be looking for the Akka.Cluster folder inside Akka.MultiNodeTestRunner\bin[Build Configuration].
So with all of the binaries in their proper place, we’re almost ready to debug! Last thing we need to do is pass some command-line arguments to the MultiNodeTestRunner.exe
via the Debug view in the Project Properties dialog.
And if we run the Akka.MultiNodeTestRunner project, viola! We can observe it successfully loading the Akka.Cluster.Tests.MultiNode.dll
and executing the tests!
You can use this technique in a lot of different cases. Macros are eternal!