Managing AssemblyVersion I
I have written a macro that autoincrements the build number of the AssemblyVersion based on the code that is available at http://blogs.biasecurities.com/jim/archive/2003/10/08/166.aspx
My macro uses ProjectItem to look for the AssemblyInfo file, uses regular expressions and supports automatically checking out and checkingIn the assemblyInfo file. I have tested it for C# projects only but i am sure with a few modifications it would work with VB.Net projects as well.
Imports EnvDTE
Imports System.Diagnostics
Public Module BuildIncrementer
' This event will be triggered after every build of a project
' You can modify the code below to only update projects that are active
' It currently will scan all projects in the solution for AssemblyInfo.cs files
' to update.
Sub IncrementBuildNumber()
'Comment the follow 3 lines, if you want the build number to increment even if the build fails
If DTE.Solution.SolutionBuild.LastBuildInfo() <> 0 Then
Exit Sub
End If
Dim skippedProjects As Integer = 0
Dim revisionNo As String = GenerateRevisionNumber()
' Change this, if you would only like to modify the AssemblyInfo file in active project files
' For Each proj As Project In DTE.ActiveSolutionProjects
For Each project As Project In DTE.Solution.Projects
'Try to get the AssemblyInfo item, if not then skip
'this project
Dim assemblyInfoItem As ProjectItem = GetAssemblyInfoItem(project)
Dim skipProject As Boolean = False
If assemblyInfoItem Is Nothing Then
skipProject = True
End If
If Not skipProject Then
Try
assemblyInfoItem.Open("{7651A701-06E5-11D1-8EBD-00A0C90F26EA}").Activate() 'vsViewKindCode
Dim build As TextRange
Dim revision As TextRange
Dim txtDoc As TextDocument = assemblyInfoItem.Document.Object("TextDocument")
GetBuildAndRevision(txtDoc, build, revision)
CheckoutActiveItem()
IncrementIfNotEmptyOrStar(build)
SetToTimeOfDayIfNotEmptyOrStar(revision, revisionNo)
assemblyInfoItem.Save()
CheckinActiveItem()
DTE.ActiveWindow.Close(vsSaveChanges.vsSaveChangesYes)
Catch ex As System.Exception
MsgBox(ex.Message)
End Try
Else
skippedProjects = skippedProjects + 1
End If
Next
System.Diagnostics.Debug.WriteLine("Skipped " & skippedProjects.ToString() & "projects")
End Sub
'Returns the full path of the first file
'in the project with the name AssemblyInfo
Function GetAssemblyInfoItem(ByVal project As Project) As ProjectItem
For Each projectItem As ProjectItem In project.ProjectItems
Dim i As Integer
For i = 1 To projectItem.FileCount
Dim filepath As String
filepath = projectItem.FileNames(i)
Dim indexOfLastSep As Integer = 1
indexOfLastSep = filepath.LastIndexOf("\")
If indexOfLastSep < 1 Then indexOfLastSep = 1
Dim fileName As String = filepath.Substring(indexOfLastSep)
If fileName.IndexOf("AssemblyInfo") > 0 Then
Return projectItem
End If
Next
Next
Return Nothing
End Function
Sub GetBuildAndRevision(ByVal textDocument As TextDocument, ByRef build As TextRange, ByRef revision As TextRange)
Dim txtrgs As TextRanges
build = Nothing
revision = Nothing
'Regular Expressions use VC6 syntax and not the new .Net syntax
If textDocument.StartPoint.CreateEditPoint().FindPattern("AssemblyVersion\("":d+\.:d+\.{(:d+|\*)}(\.{(:d+|\*)})*""", vsFindOptions.vsFindOptionsRegularExpression, Nothing, txtrgs) Then
If (txtrgs.Count > 1) Then
build = txtrgs.Item(2)
ElseIf txtrgs.Count > 2 Then
revision = txtrgs.Item(3)
End If
End If
End Sub
'Generates a revision number for this build by using the curent time
Function GenerateRevisionNumber() As String
Dim now As Date = DateAndTime.Now
Dim hrs As String = now.Hour.ToString()
Dim mins As String = now.Minute.ToString()
Dim secs As String = now.Second.ToString()
Return hrs & mins & secs
End Function
'Retrevies the text from a text range
Function GetTextFromTextRange(ByVal textRange As TextRange) As String
If textRange Is Nothing Then
Return ""
Else
Return textRange.StartPoint.GetText(textRange.EndPoint)
End If
End Function
'Retrieves the text from the range and if it star or empty then
'returns otherwise increments it
Sub IncrementIfNotEmptyOrStar(ByVal build As TextRange)
'If build is * or empty then ignore
If (Not build Is Nothing) AndAlso (Not GetTextFromTextRange(build).Equals("*")) Then
Dim buildNo As Integer = CType(GetTextFromTextRange(build), Integer)
buildNo = buildNo + 1
build.StartPoint.ReplaceText(build.EndPoint, buildNo.ToString(), sEPReplaceTextOptions.vsEPReplaceTextAutoformat)
End If
End Sub
'Retrieves the text from the range and if it star or empty then
'returns otherwise sets it to the time of day
Sub SetToTimeOfDayIfNotEmptyOrStar(ByVal revision As TextRange, ByVal revisionNo As String)
'If revision is * or empty then ignore
If (Not revision Is Nothing) AndAlso (Not GetTextFromTextRange(revision).Equals("*")) Then
revision.StartPoint.ReplaceText(revision.EndPoint, revisionNo, vsEPReplaceTextOptions.vsEPReplaceTextAutoformat)
End If
End Sub
Sub CheckoutActiveItem()
Dim checkoutCmd As Command = DTE.Commands.Item("File.CheckOutDynamicSilent")
'Check if the item is already checked out or not
If Not checkoutCmd.IsAvailable Then
System.Diagnostics.Debug.WriteLine("Checkout cmd not available")
Return
End If
Dim customin, customout
Try
DTE.Commands.Raise(checkoutCmd.Guid, checkoutCmd.ID, customin, customout)
Catch ex As System.Exception
System.Diagnostics.Debug.WriteLine(ex)
End Try
End Sub
Sub CheckinActiveItem()
Dim checkinCmd As Command = DTE.Commands.Item("File.CheckInDynamicSilent")
'Check if the item is already checked out or not
If Not checkinCmd.IsAvailable Then
System.Diagnostics.Debug.WriteLine("Checkin cmd not available, something is wrong")
Return
End If
Dim customin, customout
DTE.Commands.Raise(checkinCmd.Guid, checkinCmd.ID, customin, customout)
End Sub
Also if you want this macro to be automatically executed when the build finishes add this code to the EnvironmentEvents module that gets automatically generated whenever you create a new Macro project
Private Sub BuildEvents_OnBuildDone(ByVal Scope As EnvDTE.vsBuildScope, ByVal Action As EnvDTE.vsBuildAction) Handles BuildEvents.OnBuildDone
IncrementBuildNumber()
End Sub