Sunday, April 27, 2008

Depend on Dependency Macros for Visual Studio Project Dependencies


[update: fixed code typo]

I set dependencies on visual studio projects frequently. Here is some macro code that simplifies that process.

At the last 2 studios I joined one of the first things I did was to make an "All" solution. A single visual studio solution containing all projects required to build the game (or in Gamebryo's case, engine, samples, & tools).

A nice big solution like that needs to have dependencies set right. Visual Studio's interface for doing this is inefficient for anything but toy "solutions". But, they gave us macros, and it's easy to set dependencies with those.

Example solution file, with projects broken into logical groups

With these macros, you first select a group you would like to change the dependency information for. Run macro Step1. Then, select the group of projects that all the previous projects depend on. You'll then see this window:
Macro Dialog confirming dependencies about to be set.

Setting dozens or hundreds of project dependencies is a breeze with these macros.

Here's the code (Visual Studio 2005 macros, aka VS8):


Imports System
Imports EnvDTE
Imports EnvDTE80
Imports System.Diagnostics

' Need List:
Imports System.Collections.Generic

Public Module Module1
Sub Step1_SelectDependentProjects()
'DESCRIPTION: Step 1 of 2 for setting dependencies on projects
SelectedProjects = GetSelectedProjects()
Dim OutputString As String
OutputString = SelectedProjects.Count.ToString
OutputString += " Selected Projects:" + vbLf
OutputString += GetStringOfEachProject(SelectedProjects)
MsgBox(OutputString)
End Sub

Sub Step2_SelectDependeeProjects_AssignDependencies()
'DESCRIPTION: Step 2 of 2 for setting dependencies on projects
If SelectedProjects Is Nothing Then
MsgBox("You must first select projects to have dependencies set on, with SelectDependentProjects macro")
Return
End If

Dim DependentProjs As List(Of EnvDTE.Project) = SelectedProjects
Dim DependeeProjs As List(Of EnvDTE.Project) = GetSelectedProjects()

Dim OutputString As String
OutputString = "Are you sure you want to set" + vbLf + vbLf
OutputString += DependentProjs.Count.ToString + " Projects:" + vbLf
OutputString += GetStringOfEachProject(DependentProjs) + vbLf + vbLf

OutputString += "As dependent upon" + vbLf + vbLf
OutputString += DependeeProjs.Count.ToString + " Projects:" + vbLf
OutputString += GetStringOfEachProject(DependeeProjs) + vbLf + vbLf

If MsgBox(OutputString, MsgBoxStyle.OkCancel) = MsgBoxResult.Cancel Then
Return
End If

For Each Dependent As EnvDTE.Project In DependentProjs
For Each Dependee As EnvDTE.Project In DependeeProjs
Try
DTE.Solution.SolutionBuild.BuildDependencies.Item(Dependent).AddProject(Dependee.UniqueName)
Catch ex As System.Exception
Dim Result As Microsoft.VisualBasic.MsgBoxResult
Result = MsgBox("Failed to add dependency: " + vbLf _
+ "Dependent: " + Dependent.Name + vbLf _
+ "on" + vbLf _
+ "Dependee: " + Dependee.Name + vbLf + vbLf _
+ "Error is:" + vbLf + ex.Message + vbLf + vbLf _
+ "CONTINUE????", MsgBoxStyle.YesNo)
If Result = MsgBoxResult.No Then
Return
End If
End Try
Next
Next
MsgBox("Done.")
End Sub

' Storage for list of projects:
Public SelectedProjects As List(Of EnvDTE.Project)

' Helper functions:
Function GetSelectedProjects() As List(Of EnvDTE.Project)
Dim projs As List(Of EnvDTE.Project) = New List(Of EnvDTE.Project)
For Each selectedItem As EnvDTE.SelectedItem In DTE.SelectedItems
Try ' to get projects
If Not selectedItem.Project Is Nothing Then
If Not projs.Contains(selectedItem.Project) Then
projs.Add(selectedItem.Project)
End If
End If
Catch
End Try
Next
Return projs
End Function

Function GetStringOfEachProject(ByVal ProjectsList As List(Of EnvDTE.Project)) As String
Dim OutputString As String = ""
For Each proj As EnvDTE.Project In ProjectsList
If OutputString.Length > 0 Then ' add new line
OutputString += vbLf
End If
OutputString += " " + proj.Name
Next
Return OutputString
End Function

End Module