Built a TeamCity VB6.exe output parser
This post follows a previous post where I built a devenv.com output parser based on code from Wolfgang Kleinschmit. I've modified the code now to parse the output from a Visual Basic 6 compile so that TeamCity knows whether or not a build failed and why. I compile this program and place it in the same directory as VB6.exe. Then from TeamCity, I will use a command line build step that calls the compiled application instead of VB6.exe.
Sample command line build step custom script:
vb6buildrunner.exe /m "project1.vbp"Make sure that the build machine knows the path to vb6buildrunner.exe (perhaps put the path to the executable in the environment path variable).
Below is the code for VB6 output parser. Just create a VB.Net console app and paste the below code into it.
Imports System.IO
Imports System.Text
Imports System.Text.RegularExpressions
Imports System.Threading
Module Program
Private projectName As String
Private logFile As String
Private commandLineFail As Boolean
Private buildFailed As Boolean
Sub Main()
If My.Application.CommandLineArgs.Count > 0 Then
For i = 0 To My.Application.CommandLineArgs.Count - 1
Dim a As String = My.Application.CommandLineArgs(i)
If String.Compare(a, "/out", True) = 0 OrElse _
String.Compare(a, "/r", True) = 0 OrElse _
String.Compare(a, "/run", True) = 0 OrElse _
String.Compare(a, "/runexit", True) = 0 Then
ProcessLine(String.Format("ERROR: Argument '{0}' is not allowed by the build runner.", a))
commandLineFail = True
ElseIf String.Compare(a, "/make", True) = 0 OrElse _
String.Compare(a, "/m", True) = 0 Then
projectName = My.Application.CommandLineArgs(i + 1)
logFile = projectName & ".makelog"
End If
Next
End If
Console.Out.WriteLine("##teamcity[progressMessage 'VB6 Compiling for project {0}']", projectName)
Console.Out.Flush()
If Not commandLineFail Then
If File.Exists(logFile) Then
File.Delete(logFile)
logFile &= "1"
ElseIf File.Exists(logFile & "1") Then
File.Delete(logFile & "1")
End If
Dim pattern As String = String.Format("^[""]?({0})[""]?\s*", Environment.GetCommandLineArgs()(0).Replace("\", "\\"))
Dim rgx As New Regex(pattern)
Dim argumentsOnly As String = rgx.Replace(Environment.CommandLine, String.Empty)
argumentsOnly &= String.Format(" /out ""{0}""", logFile)
Dim psi As New ProcessStartInfo("vb6.exe")
psi.Arguments = argumentsOnly
psi.CreateNoWindow = True
psi.ErrorDialog = False
psi.RedirectStandardError = True
psi.RedirectStandardOutput = True
psi.UseShellExecute = False
Dim P As Process = Process.Start(psi)
Dim StdOReader As New Thread(New ParameterizedThreadStart(AddressOf ReadStd))
StdOReader.Start(P.StandardOutput)
Dim StdEReader As New Thread(New ParameterizedThreadStart(AddressOf ReadStd))
StdEReader.Start(P.StandardError)
P.WaitForExit()
Environment.ExitCode = P.ExitCode
StdOReader.Join()
StdEReader.Join()
Dim objReader As New StreamReader(logFile)
Try
Dim sLine As String
Do
sLine = objReader.ReadLine()
If Not String.IsNullOrWhiteSpace(sLine) Then
ProcessLine(sLine)
End If
Loop Until sLine Is Nothing
Finally
objReader.Close()
End Try
Else
Environment.ExitCode = 1
End If
For Each key As Integer In flows.Keys
Dim lines As List(Of String) = flows(key)
For Each tcLine As String In lines
Console.Out.WriteLine(tcLine)
Next
Next
If buildFailed Then
Console.Out.WriteLine("##teamcity[message status='FAILURE' errorDetails='' text='[000|] Build failed.']")
End If
Console.Out.WriteLine("+++++++++++++++++++++++++++++++++++++++++++++")
Console.Out.Flush()
End Sub
Sub ReadStd(obj As Object)
Dim R As TextReader = DirectCast(obj, TextReader)
Dim line As String = R.ReadLine
Do While (line IsNot Nothing)
ProcessLine(line)
line = R.ReadLine
Loop
End Sub
Public rxMessage As New Regex("succeeded", RegexOptions.Compiled)
Public flows As New Dictionary(Of Integer, List(Of String))
Sub ProcessLine(line As String)
Dim flowID As Integer = 0
Dim status As String = "ERROR"
If String.IsNullOrWhiteSpace(line) Then
Return
End If
If line.StartsWith("Build of ") AndAlso line.EndsWith(" succeeded.") Then
status = "NORMAL"
Else
buildFailed = True
End If
line = QuoteLine(line)
If (Not flows.ContainsKey(flowID)) Then
flows(flowID) = New List(Of String)()
End If
Dim fmtLine As String = String.Format("##teamcity[message status='{2}' errorDetails='' text='[{0:000}|] {1}']", flowID, line, status)
flows(flowID).Add(fmtLine)
End Sub
Function QuoteLine(line As String) As String
Dim sb As New StringBuilder
For Each c As Char In line
If ("'|]" & Environment.NewLine).IndexOf(c) <> -1 Then
sb.Append("|"c)
End If
sb.Append(c)
Next
line = sb.ToString()
Return line
End Function
End Module