This article was contributed by: JamesB.
Environment: Visual Studio .NET
Introduction
Each of us who migrate to VS.NET encounters a few bumps in the road. One bump that caught me off guard was that my macros no longer worked.
My existing build number incrementing macro was a combination of bits and pieces of code scavenged from countless authors over the years. Although my build numbering system is not complicated, and does not require features found in similar macros, it will provide a solid foundation to start from.
Goals
These are my requirements for automating build numbers:
- On each release build, automatically increment a build number contained in a separate file that will be included into my project.
- Automatically track build date and number in a log file.
Code Discussion
My first task was to find a replacement for the .Application_BeforeBuildStart(). event handler used in VS 6. The designers of VS.NET provide us with four easy-to-use build events:
- OnBuildBegin
- OnBuildDone
- OnBuildProjConfigBegin
- OnBuildProjConfigDone
The naming convention used for the event handlers is somewhat misleading. We don’t want to use OnBuildBegin because it is called one time, even if we are building multiple configurations (release, debug, and so forth), making it hard to increment the build number for release builds only. OnBuildProjConfigBegin is called for each configuration being built, and supplies a ProjectConfig string containing the name of the project configuration used (release, debug, and so on).
Most of the work is done in the OnBuildProjConfigBegin event handler with the aid of two helper macros:
- WriteToLogFile
- WriteToOutputBuildPane
Both helper methods could be folded into the OnBuildProjConfigBegin event handler, or removed if not required.
Code
' ------------------------------------ ' OnBuildProjConfigBegin event handler ' ------------------------------------ Private Sub BuildEvents_OnBuildProjConfigBegin( ByVal Project As String, ByVal ProjectConfig As String, ByVal Platform As String, ByVal SolutionConfig As String) Handles BuildEvents.OnBuildProjConfigBegin ' abort if build type is debug If InStr(1, ProjectConfig, "Debug", 1) Then Exit Sub ' get ver filename Dim res_filename As String res_filename = DTE.Solution.FullName res_filename = Path.ChangeExtension(res_filename, ".ver") ' open VERSION FILE and increment build number Dim msg_text As String If File.Exists(res_filename) Then Dim line As String Try Dim sr As StreamReader = New StreamReader(res_filename) line = sr.ReadLine() sr.Close() Catch ex As Exception Module1.WriteToOutputBuildPane(vbCrLf & _ "Version file read failed : " & ex.Message & vbCrLf) End Try line = Right(line, line.Length - 18) Try Dim sw As StreamWriter = File.CreateText(res_filename) sw.WriteLine("#define BUILD_NUM {0}", line + 1) sw.Close() Catch ex As Exception Module1.WriteToOutputBuildPane(vbCrLf & _ "Version file write failed : " & ex.Message & vbCrLf) End Try msg_text = "Build number : " & line + 1 & ", " & Now Module1.WriteToOutputBuildPane(vbCrLf & msg_text & vbCrLf) Module1.WriteToLogFile(msg_text) Else Try Dim sw As StreamWriter = File.CreateText(res_filename) sw.WriteLine("#define BUILD_NUM 1") sw.Close() Catch ex As Exception Module1.WriteToOutputBuildPane(vbCrLf & _ "Version file write failed : " & ex.Message & vbCrLf) End Try msg_text = "Build number : 1, " & Now Module1.WriteToOutputBuildPane(vbCrLf & msg_text & vbCrLf) Module1.WriteToLogFile(msg_text) End If End Sub ' ---------------------------------- ' write text message to a log file ' ---------------------------------- Sub WriteToLogFile(ByVal msg_text As String) Dim log_filename As String log_filename = DTE.Solution.FullName log_filename = Path.ChangeExtension(log_filename, ".log.txt") Try Dim sw As StreamWriter = File.AppendText(log_filename) sw.WriteLine(msg_text) sw.Close() Catch ex As Exception MsgBox("Log file write failed : " & ex.Message) End Try End Sub ' ---------------------------------------------------------------- ' write a text message to the build pane of the output tool window ' ---------------------------------------------------------------- Sub WriteToOutputBuildPane(ByVal msg_text As String) ' Create a tool window handle for the Output window. Dim win As Window = DTE.Windows.Item(EnvDTE.Constants. _ qvsWindowKindOutput) ' Create handles to the Output window and its build pane. Dim OW As OutputWindow = win.Object Dim OWp As OutputWindowPane OWp = OW.OutputWindowPanes.Item("Build") ' Add a line of text to the output pane. OWp.OutputString(msg_text & vbCrLf) End Sub
Integration
Note! If you do not have a macro project, create one before continuing.
You need a macro project opened and displayed in the VS Macros IDE.
- Each macro project has an EnvironmentEvents page. Double-click to open the page.
- Select BuildEvents from the class name dropdown list.
- Select OnBuildProjConfigBegin from the method name dropdown list.
- Add two imports statements to the list of name spaces to use.
The .System. namespace is required for exceptions and .SystemIO. is for the stream reader and writer classes.
- Add the working code to the OnBuildProjConfigBegin method.
- Double click to open the Module1 page.
Module1 is a default page name the IDE uses. If you use a different name, be sure to resolve namespace issues in the OnBuildProjConfigBegin method.
- Add the two helper methods.
- Add the .System and .SystemIO. imports statements to the list of namespaces to use.
Sample Output
The following samples are the result of building a small MFC dialog based application.
Contents of the version file: (MfcApp.ver)
#define BUILD_NUM 2
Contents of the output build pane: (batch – rebuild all)
Build number : 2, 7/16/2003 4:07:35 PM ------ Rebuild All started: Project: MfcApp, Configuration: Release Win32 ------ Deleting intermediate files and output files for project 'MfcApp', configuration 'Release|Win32'. Compiling... stdafx.cpp Compiling... MfcAppDlg.cpp MfcApp.cpp Generating Code... Compiling resources... Linking... LINK : warning LNK4089: all references to 'OLEAUT32.dll' discarded by /OPT:REF Build log was saved at "file://d:\MfcApp\Release\BuildLog.htm" MfcApp - 0 error(s), 1 warning(s) ------ Rebuild All started: Project: MfcApp, Configuration: Debug Win32 ------ Deleting intermediate files and output files for project 'MfcApp', configuration 'Debug|Win32'. Compiling... stdafx.cpp Compiling... MfcAppDlg.cpp MfcApp.cpp Generating Code... Compiling resources... Linking... Build log was saved at "file://d:\MfcApp\Debug\BuildLog.htm" MfcApp - 0 error(s), 0 warning(s) ---------------------- Done ---------------------- Rebuild All: 2 succeeded, 0 failed, 0 skipped
Contents of the log file : (MfcApp.log.txt)
Build number : 1, 7/16/2003 4:07:35 PM Build number : 2, 7/16/2003 4:19:07 PM
Conclusions
The process of converting macros from VS 6 to VS.NET was both challenging and rewarding. I was somewhat surprised how nicely the DTE automation object model, VB.NET, and the .NET framework worked together.