Using Microsoft.Build.Traversal in lieu of solutions for building your projects
Somewhere recently I came across a reference to the Microsoft.Build.Traversal SDK which has been sat in an open tab in my browser ever since. Traversal:
Supports creating traversal projects which are MSBuild projects that indicate what projects to include when building your tree. For large project trees, they are replacements for Visual Studio solution files.
So, grossly over-simplifying Traversal is a way of replacing solution files with an MSBuild project, which means you can make use of all the funky stuff MSBuild can do to eliminate the need for a build.cmd or other custom scripts, at least in some circumstances.
Getting setup to try out Traversal
In order to try it out, we need a couple of projects to work with. To keep the steps to get to where we want to be short, I'm going to describe them as a series of dotnet CLI commands but they could just as easily be carried out in Visual Studio. I've run these commands from the Developer Command Prompt for VS 2017 from the folder that I store all my projects in:
md Traversal cd Traversal
dotnet new sln
dotnet new classlib -n HelperLibrary
dotnet new classlib -n OtherLibrary
dotnet new console -n ConsoleApp
dotnet new classlib -n CI
dotnet sln add .\HelperLibrary
dotnet sln add .\OtherLibrary
dotnet sln add .\ConsoleApp
dotnet sln add .\CI
dotnet add .\ConsoleApp reference .\HelperLibrary
These commands should hopefully be fairly obvious, but in summary what's being done is:
- Creating a new solution called Traversal
- Creating two class libraries called HelperLibrary and OtherLibrary
- Creating a console app called ConsoleApp
- Creating another class library called CI
- Adding all four projects to the solution
- Adding a project reference to ConsoleApp referencing HelperLibrary
Once these commands have been run it's quick and easy to run start traversal.sln to spin-up Visual Studio, though if you have Visual Studio 2017 and another earlier version installed the solution may open in the wrong one - you definitely want 2017 (or greater!) for this to work. And here's the solution/project structure as seen in VS2017:
Next up is some tirdy-up and setup:
- Remove Class1.cs from the CI project
- Rename Class1.cs in HelperLibrary to Helper and Class1.cs in OtherLibrary to Other (along with the classes inside them)
- Adding a static method to the Helper class called GetHelloWorld that returns the string "Hello, world!" (yes, I know, I know!)
- Changing the Console App so it has Console.WriteLine(HelperLibrary.Helper.GetHelloWorld());, i.e. so it uses HelperLibrary
Once that's done you can set ConsoleApp as the startup project, drop a breakpoint on the closing brace of the Main method (or add Console.ReadLine(); to the end of the method instead) and run the project to confirm that the dependencies are all being resolved and that the solution works correctly. Now that's done it's time to add Traversal into the mix.
I added the CI project in anticipation of using it as the Traversal project, adding it to the solution to make it easy to access so we'll remove it once we're done with editing it. First up, right-click on the project and choose Edit CI.csproj from the context menu. Now edit the project file so it looks like this:
<Project Sdk="Microsoft.Build.Traversal/1.0.22"> <ItemGroup> <ProjectReference Include="HelperLibrary\HelperLibrary.csproj" /> <ProjectReference Include="ConsoleApp\ConsoleApp.csproj" /> <ProjectReference Include="OtherLibrary\OtherLibrary.csproj" /> </ItemGroup> </Project>
Once the project file is complete, remove the project from the solution, move the project file to the root folder of the solution and delete the CI folder that it was living in before. You can add it as a 'Solution Item' to make it easily accessible from inside Visual Studio, if there's a way to do that via the dotnet CLI I've yet to find it! If you don't copy the project file to the solution folder you'll encounter build failures unless you update the ProjectReference's to point to ..\ (i.e. move them up one parent directory to where the project folders are sat)
All that's left to do now is to build the project, which is:
dotnet build CI.csproj -c Debug
Or of course, -c Release if you want to go for a release build! With a little bit of luck you should ses output something along the lines of:
D:\Git\traversal>dotnet build CI.csproj -c Debug Microsoft (R) Build Engine version 15.8.166+gd4e8d81a88 for .NET Core Copyright (C) Microsoft Corporation. All rights reserved. Restore completed in 86.03 ms for D:\Git\traversal\HelperLibrary\HelperLibrary.csproj. Restore completed in 86.04 ms for D:\Git\traversal\ConsoleApp\ConsoleApp.csproj. Restore completed in 86.04 ms for D:\Git\traversal\OtherLibrary\OtherLibrary.csproj. HelperLibrary -> D:\Git\traversal\HelperLibrary\bin\Debug\netstandard2.0\HelperLibrary.dll ConsoleApp -> D:\Git\traversal\ConsoleApp\bin\Debug\netcoreapp2.1\ConsoleApp.dll OtherLibrary -> D:\Git\traversal\OtherLibrary\bin\Debug\netstandard2.0\OtherLibrary.dll Build succeeded. 0 Warning(s) 0 Error(s) Time Elapsed 00:00:01.65
What you do with Microsoft.Build.Traversal is up to you, but there's a whole load of extensibilty points listed in the Microsoft.Build.Traversal SDKs README.md on GitHub.
My skillset has matured somewhat since then, which you'll probably see from the posts here. You can read a bit more about me on the about page of the site, or check out some of the other posts on my areas of interest.