summaryrefslogtreecommitdiff
path: root/utils/source_builder/source_build.py
blob: e6ba7e24c89da317242555badb21b3661a61a430 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
#====== Copyright 1996-2005, Valve Corporation, All rights reserved. =======
#
# Purpose: Syncs, builds and runs tests on the steam code in
#          the currently active p4 clientspec
#
#=============================================================================

# INSTALLATION INSTRUCTIONS:
#
# 1. add the vc compiler solution compiler devenv.exe to the path (defaults to being in "C:\\Program Files\\Microsoft Visual Studio .NET 2003\\Common7\\IDE\\devenv.exe")
# 2. create a P4 clientspec that contains the steam code, eg. //steam/main/...
# 3. make that the default clientspec
# 4. sync to it
# 5. run this build script from inside that directory


import sys, os, string, re, time, smtplib, getopt, P4, SystemHelpers

# config options
g_bTest = 0                                     # 1: disables reporting to SRCDEV; only reports to admin
g_bRunTests = 0                                 # 1: enables testing after build is complete
g_bSync = 0                                     # 1: enables syncing to perforce 
g_bLockMutex = 0                                # 1: require mutex to be free before proceeding to build
g_bBuild = 0                                    # 1: build binaries
g_bDebug = 0                                    # 1: enables debug output
g_bDev = 0                                      # 1: script builds immediately upon start, regardless of presence of perforce state
g_bShaders = 0
g_bXBOX = 0

g_szEmailAlias = ""     # email alias of users interested in build failures
g_szAdministrator = ""   # address to auto-email if the script fails for some reason, test output
g_szMailhost = "" 	# set to hostname of machine running an SMTP server
g_szSenderEmail = ""     # email address that the failure mails come from; TODO: Get buildmachine email 

g_szBuildExe = ""                   # executable for compilation; devenv: .Net, BuildConsole: IncrediBuild
#g_szBuildExe = "devenv"                        # executable for compilation; devenv: .Net, BuildConsole: IncrediBuild
g_szBuildType = ""                      # build type; /rebuild or /build
g_szBuildFlags = ""                        # should be empty for devenv, /all for buildconsole

g_szTestingFile = ""               # name of testing file, located in .\Build Machine Tests\
g_szLocalLogFile = ""               # name of generated log file; currently not used
g_szPublishedErrorsDir = ""  # place to copy log file to reference in failure email
g_szTestDirectory = ""     # directory where the testing files are located

g_szBaseDir = os.getcwd()                       # directory from which the script is launched

g_szP4SrcFilesToWatch = ""     # path to src files to watch
g_szP4ForceFilesToWatch = ""    # path to bin files to watch
g_szP4SyncFilesToWatch = ""
g_szP4Mutex = ""                        # name of perforce mutex to wait for
g_szP4ChangeCounter = ""		# perforce counter containg the changelist number we're verifying
g_szP4VerifiedCounter = ""		# perforce counter the last changelist number we have verified

g_nP4MostRecentCheckin = 0                      # used to keep track of current checkin
g_nP4LastVerifiedCheckin = 0                    # used to keep track of last verified checkin

g_nSleepTimeBetweenChecks = 10                  # number of seconds to wait before checking for a free mutex

g_szBuildName = ""


# mails off a success checkin
def SendSuccessEmail( _szEmail, _szChangeNumber ):
    cMailport = smtplib.SMTP(g_szMailhost)
    szTestString = ""
    if g_bTest:
        szTestString = "TEST"
    print "sending success email to: " + _szEmail

    szMessage = 'From: ' + g_szBuildName + ' Builder ' + ' <' + g_szSenderEmail + '>\n' +\
              'To: ' + szTestString + _szEmail + '\n' +\
              'Subject: [ ' + g_szBuildName + ' build successful ]' +\
              '\n' +\
              '\n' +\
              'Your checkin:\n\n' +\
              _szChangeNumber +\
              '\n\n' +\
              'has been successfully built and verified against all tests.\n'

    if ( g_bTest | g_bDev ):
        cMailport.sendmail( g_szSenderEmail, g_szAdministrator, szMessage )
    else:       
        cMailport.sendmail( g_szSenderEmail, _szEmail, szMessage )
    cMailport.quit()

# print of debugging messages
def dPrint( _szText ):
    if g_bDebug:
        print _szText

# mails off the current error string
def SendFailureEmail( _szErr ):
    cMailport = smtplib.SMTP( g_szMailhost )
    szTestString = ""
    if g_bTest:
        szTestString = "TEST"
    print "sending failure email to: " + g_szEmailAlias
    print "failure reason: " + _szErr

    szMessage = 'From: ' + g_szBuildName + ' Builder' + ' <' + g_szSenderEmail + '>\n' +\
              'To: ' + szTestString + g_szEmailAlias + '\n' +\
              'Subject: [ ' + g_szBuildName + ' branch broken ]' +\
              '\n' +\
              '\n' +\
              _szErr +\
              '\n' +\
              P4.GetUnverifiedCheckins( g_szP4SrcFilesToWatch, g_szP4VerifiedCounter, g_szP4ChangeCounter )              

    if g_bTest | g_bDev:
        cMailport.sendmail( g_szSenderEmail, g_szAdministrator, szMessage )
    else:
        cMailport.sendmail( g_szSenderEmail, szTestString + g_szEmailAlias, szMessage )
    cMailport.quit()

# use to email the admin about any unexpected errors that occur
def ComplainToAdmin( _szErr ):
    cMailport = smtplib.SMTP(g_szMailhost)
    print "sending script failure email to: " + g_szAdministrator
    print "failure reason: |" + _szErr + "|"

    szMessage = 'From: ' + g_szBuildName + " Builder" + ' <' + g_szSenderEmail + '>\n' +\
              'To: ' + g_szAdministrator + '\n' +\
              'Subject: [ ' + g_szBuildName + ' builder build script error ]' +\
              '\n' +\
              '\n' +\
              'error reason:\n' +\
              _szErr +\
              '\n'
              
    cMailport.sendmail(g_szSenderEmail, g_szAdministrator, szMessage)
    cMailport.quit()

# parses out the reason for failure from a test
def GetTestFailureReason( _nBuild ):

    # start our error message
    szMsg = "Test log file:\n"

    # make a directory to copy the error files into, based on the build config and the p4 changelist number
    nBuildNum = P4.GetCounter(g_szP4ChangeCounter)
    szErrDir = g_szPublishedErrorsDir + nBuildNum + "\\" + _nBuild
    os.popen("mkdir " + szErrDir, "r").read()
    
    # copy all the files to the errors dir
    os.popen("copy " + _nBuild + "\\*.*" + " " + szErrDir + " /Y", "r").read()
    # add the error log to the failure message
    szMsg += "    " + szErrDir + "\\" + g_szLocalLogFile + "\n"

    # add the minidumps to the failure message
    rgMinidumps = string.split( os.popen("dir " + _nBuild + "\\*.mdmp /b", "r").read(), "\n" )
    if len(rgMinidumps) > 1:
        szMsg += "\nCrash dump:\n"
        for szMinidump in rgMinidumps:
            if len(szMinidump) > 16:
                szMsg += "    " + szErrDir + "\\" + szMinidump + "\n"
        szMsg += "To view the minidump, click the link above then click \"open\" on the next dialog.\nHit \"F5\" in the now open debugger.  When it asks for the source code, change the start of the suggested path from \"c:\\\" to \"\\\\steam3builder\\\".\n"

    # delete the minidumps, so they don't confuse us next time
    os.popen("del " + _nBuild + "\\*.mdmp", "r").read()
        

    # parse out the error from the logfile 
    szLog = os.popen( "type " + _nBuild + "\\" + szLocalLogFile, "r").read()
    szLogLines = []
    szLogLines = string.split( szLog, "\n" )
    bFoundErr = 0
    for szLine in szLogLines:
        aszToken = string.split(szLine, ' ')
        if len( aszToken ) > 3:
            if aszToken[0] == "***" and aszToken[1] == "TEST" and aszToken[2] == "FAILED":
                # probably an error line, add to the list
                szMsg += szLine
                szMsg += "\n"
                bFoundErr = 1

    # if we haven't found any error lines, use the last line of the file
    if bFoundErr == 0 and len(szLogLines) > 0:
        szMsg += szLogLines[len(szLogLines) - 1]

    return szMsg


# performs a single test run of the specified exe with the specified test parameters
def RunTest( _szCommand ):

    aszParms = string.split( _szCommand, " ", 1)
    print "running " + _szCommand
    print os.popen( _szCommand, "r" ).read()

    # return success
    return aszParms[0]

def RunCompare( _szCommand ):
    szResult = os.popen( _szCommand, "r" ).read()
    return szResult

def SearchFileForErrors( _szFile, _szError ):
    print ("Searching " + _szFile + " for occurences of " + _szError + ".\n")
    bIncludeNext = 0
    szErrorResults = ""
    aszFileLines = string.split( os.popen( "type " + _szFile, "r").read(), "\n" )

    for szLine in aszFileLines:
        if ( bIncludeNext == 1 ):
            szErrorResults += szLine + "\n"
            bIncludeNext = 0
        aszParms = string.split(szLine, " ", 1)
        if (aszParms[0] == _szError):
            #there is a UnitTest error, warning, or assert
            szErrorResults += "\n" + szLine + "\n"
            bIncludeNext = 1;
    return szErrorResults
 
# runs a set of tests as defined by the test script file
def RunTestScript( ):
    print ("Enter Testing")
    # make sure we're in the right dir
    SystemHelpers.ChangeDir("..")
    # load the script
    szReturn = os.popen( "RunTestScripts.py" ).read()
    return szReturn

# runs a single build, and runs the tests on the build if specified
def RunBuild( _szBuild ):
    # launch devstudio to build the solution
    szCmd = _szBuild
    aszToken = string.split(szCmd, " ", 3)

    if aszToken[0] == "devenv":
        #we are building this. Find out what it is.
        if aszToken[2] == "/project":
            #building a project.  Grab it.
            aszToken = string.split(szCmd, " ", 5)
            szCmd = g_szBuildExe + " " + aszToken[1] + " /project " + aszToken[3] + " " + g_szBuildType + " " + aszToken[5] 
        else:
            #not build a specific project.  Probably everything under lostcoast.
            szCmd = g_szBuildExe + " " + aszToken[1] + " " + g_szBuildType + " " + aszToken[3] + " " + g_szBuildFlags
    else:
        #didn't see the devenv line; not building this but it isn't an error either.
        return ""
                
    print "building: " + szCmd
    szOutput = os.popen(szCmd, "r").read()
    # parse the output for any errors
    aszOutputLines = string.split(szOutput, "\n")
    bSuccess = 1    
    szBuildErr = "\n\n\nError building configuration " + _szBuild + ":\n"

    for szLine in aszOutputLines:
        if g_bDebug:
            print szLine;
        aszTokens = string.split(string.lstrip(szLine), ' ', 4)
        if len(aszTokens) > 1:
            if aszTokens[0] == "--------------------Configuration:":
                bPrintedProject = 0
                szProject = aszTokens[1]
            if aszTokens[1] == ":" and aszTokens[2] == "error" and aszTokens[3] != "PRJ0019":
                # probably an error line, add to the list
                count = string.count( szBuildErr, aszTokens[4])
                if ( count == 0 ):
                    if ( bPrintedProject == 0 ):
                        szBuildErr += "Project: " + szProject + "\n"
                        bPrintedProject = 1                    
                    szBuildErr += szLine + "\n"
                #err2 += szLine + "\n"
                bSuccess = 0
            if aszTokens[1] == ":" and aszTokens[2] == "error" and aszTokens[3] == "PRJ0019":
                # the delete error line, add to the list
                szBuildErr += "IGNORE: "
                szBuildErr += szLine + "\n"
                #err2 += szLine + "\n"
        aszTokens = string.split(string.lstrip(szLine), ' ')        
        if len(aszTokens) > 4:
            # can't do this: the weird delete error will trigger this and we need to ignore that
            # check that the "Rebuild All: x succeeded, y failed, z skipped line says no failures
            #if szT[0] == "Rebuild" and szT[3] rrorMessages + "The " + szTestName + " test failed\nThe error is " + szCompareLine
            #if szErrorMessages== "succeeded," and szT[5] == "failed," and not szT[4] == "0":
                    # failure
            #       bSuccess = 0
            # check for linker errors
            if aszTokens[0] == "LINK" and aszTokens[1] == ":" and aszTokens[2] == "fatal" and aszTokens[3] == "error":
                if ( bPrintedProject == 0 ):
                    szBuildErr += "Project: " + szProject + "\n"
                    bPrintedProject = 1 
                # linker error line, add to the list
                szBuildErr += szLine + "\n"
                #err2 += szLine + "\n"
                bSuccess = 0
            # can't do this either:  Delete file problem
            # check the standard error dealie.
            # if szT[1] == '-' and szT[3] == "error(s)," and szT[2] != '0':
                #we have a build error here
            #    err += szLine
            #    err += "\n"
            #check for fatal error
            if aszTokens[2] == "fatal" and aszTokens[3] == "error":
                if ( bPrintedProject == 0 ):
                    szBuildErr += "Project: " + szProject + "\n"
                    bPrintedProject = 1 
                #fatal error
                szBuildErr += szLine + "\n"
                #err2 += szLine + "\n"
                bSuccess = 0
    
    # return immediately if failed    
    if not bSuccess:
        print szBuildErr
        szBuildErr + "\n\n"
        #ComplainToAdmin(err2)
        return szBuildErr

    return ""

def RunBuildBatch():    
    bSuccess = 1
    #aszBatchBuildLines = string.split( os.popen( "type " + szBatchFile, "r").read(), "\n" )
    #bIsDevLine = 0
    szBuildErrs = ""
    szBuildResult = RunBuild( "devenv everything.sln /build \"debug|win32\" " )

    szBuildResult += RunBuild( "devenv everything.sln /build \"release|win32\" " )
    szTestResult = RunTestScript()
    if szBuildResult != "":
        szBuildErrs += szBuildResult
        bSuccess = 0
    if szTestResult != "\n":
        szBuildErrs += szTestResult
        bSuccess = 0
    if not bSuccess:
        SendFailureEmail(szBuildErrs)
    return bSuccess   
    
    
# runs all the builds
def RunAllBuilds():    
    bSuccess = 1
    szBuildErrs = ""
    SystemHelpers.ChangeDir("\game")
    if g_bBuild:
        SystemHelpers.ChangeDir("\src")
                
        # build the shadercompiler and all shaders
        if g_bShaders:
            print( "Compiling shadercompile" )
            os.system( "devenv shadercompile.sln /rebuild release > silence" )
            # should check here to make sure the shadercompile worked
            SystemHelpers.ChangeDir("\\src\\materialsystem\\stdshaders")
            print( "Building shaders" )
            child_stdin, child_stdout, child_stderr = os.popen3( "buildallshaders.bat" )
            print( child_stdout.read() )
            szSomeShaderErr = child_stderr.read()
            szShaderErrors = ""
            aszShaderLines = string.split( szSomeShaderErr, "\n" )
            for szLine in aszShaderLines:
                dPrint( szLine )
                nCount = string.count( szLine, 'U1073:' )
                if nCount > 0:
                    aszToken = string.split( szLine, " " )
                    szShaderName = aszToken[9]
                    szShaderErrors = szShaderErrors + "The shader file " + szShaderName + " is missing and failed during buildallshaders.bat.\n"
                    if szShaderErrors:
                        SendFailureEmail( szShaderErrors )

        SystemHelpers.ChangeDir("\\src")
        bSuccess = bSuccess & RunBuildBatch();

#XBOX Section
        if g_bXBOX:            
            szXBoxOutput = RunBuild( "devenv source_x360.sln /rebuild \"release|xbox 360\"" )
            if "\n\n\nError building configuration " + "devenv source_x360.sln /rebuild release" + ":\n" != szXBoxOutput:
                bSuccess = 0
                SendFailureEmail( szXBoxOutput )
     #delete tier0.dll 
        
        
    if g_bRunTests:
        szTestErrors = RunTestScript()
        if szTestErrors != "\n":
            # success = 0
            ComplainToAdmin( szTestErrors )
    return bSuccess

# builds from a local branch and runs a subset of tests
def PerformSourceBuild():

    # build and test in each configuration
    if not RunAllBuilds():
        print "Source build failed"
        return 0

    print "Source build: SUCCESS"
    return 1
    

# syncs, builds, runs tests
def PerformDailyBuild():
    print "  changes detected, starting daily build"
    # update the counter to be what we're verifying
    change = P4.SubmittedChangelist( g_szP4SrcFilesToWatch )
    g_nP4MostRecentCheckin = change
    g_nP4LastVerifiedCheckin = P4.GetCounter(g_szP4VerifiedCounter)
    if g_nP4MostRecentCheckin and g_nP4LastVerifiedCheckin:
        print "Most recent checkin is " + g_nP4MostRecentCheckin + "\n"
        print "Last verified checkin is " + g_nP4LastVerifiedCheckin + "\n"
    # the p4 command can occasionally fail to deliver a valid changelist number, unclear why
    # can't update the counter, it just means we'll run twice
    if change:
        P4.SetCounter(g_szP4ChangeCounter, change)
    # sync to the new files
    if ( g_bSync ):
        SystemHelpers.ChangeDir("\\src")
        print( "Cleaning\n" )
        os.system("cleanalltargets.bat > silence")
        SystemHelpers.ChangeDir("\\")        
        print "Synching force files."
        P4.Sync( g_szP4ForceFilesToWatch, 1 )
        print "Synching other files."
        P4.Sync( g_szP4SyncFilesToWatch, 0 )
        print( "Setting up VPC" )
        os.system("setupVPC.bat")
        
    #P4.UnlockMutex(g_szP4Mutex)
    # build and test in each configuration
    if not RunAllBuilds():
        print "Daily build failed"
        return
        
    # send a success email, from past the last successful checkin to the current
    if change:
        szVerifiedOrig = P4.GetCounter(g_szP4VerifiedCounter)
        if szVerifiedOrig:
            szVerifiedPlusOne = str( int( szVerifiedOrig ) + 1 )
            changes = P4.GetChangelistRange(szVerifiedPlusOne, change, g_szP4SrcFilesToWatch );
            for ch in changes:
                if len(ch) > 1:
                    szEmail = P4.GetEmailFromChangeLine(ch)
                    SendSuccessEmail(szEmail, ch)
                    #SendSuccessEmail("jason", ch)
            # remember this change that we've verified
            P4.SetCounter(g_szP4VerifiedCounter, change)

    print "Daily build: AN UNEQUIVOCAL SUCCESS"

def PrintConfig():
    print("Configuration:")
    print("test = " + str(g_bTest))
    print("run_tests = " + str(g_bRunTests))
    print("lock_mutex = " + str(g_bLockMutex))
    print("build = " + str(g_bBuild))
    print("debug = " + str(g_bDebug))
    print("dev = " + str(g_bDev))
    print("shaders = " + str(g_bShaders))
    print("sync = " + str(g_bSync))
    print("email_alias = " + g_szEmailAlias)
    print("admin_email = " + g_szAdministrator)
    print("mail_host = " + g_szMailhost)
    print("sender_email = " + g_szSenderEmail)
    print("build_exe = " + g_szBuildExe)
    print("build_type = " + g_szBuildType)
    print("build_flags = " + g_szBuildFlags)
    print("test_file = " + g_szTestingFile)
    print("log_file = " + g_szLocalLogFile)
    print("error_dir = " + g_szPublishedErrorsDir)
    print("test_dir = " + g_szTestDirectory)
    print("src_files = " + g_szP4SrcFilesToWatch)
    print("force_files = " + g_szP4ForceFilesToWatch)
    print("sync_files = " + g_szP4SyncFilesToWatch)
    print("mutex = " + g_szP4Mutex)
    print("change_counter = " + g_szP4ChangeCounter)
    print("verify_counter = " + g_szP4VerifiedCounter)
    print("build_name = " + g_szBuildName)

def ParseConfigFile(configFileName):
     aszBatchBuildLines = string.split( os.popen( "type " + configFileName, "r").read(), "\n" )
     global g_bTest
     global g_bRunTests
     global g_bLockMutex
     global g_bBuild
     global g_bDebug
     global g_bDev
     global g_bShaders
     global g_bSync
     global g_szEmailAlias
     global g_szAdministrator
     global g_szMailhost
     global g_szSenderEmail
     global g_szBuildExe
     global g_szBuildType
     global g_szBuildFlags
     global g_szTestingFile
     global g_szLocalLogFile
     global g_szPublishedErrorsDir
     global g_szTestDirectory
     global g_szP4SrcFilesToWatch
     global g_szP4ForceFilesToWatch
     global g_szP4SyncFilesToWatch
     global g_szP4Mutex
     global g_szP4ChangeCounter
     global g_szP4VerifiedCounter
     global g_szBuildName
     for szLine in aszBatchBuildLines:
         aszTokens = string.split(string.lstrip(szLine), ' ', 2)
         firstToken = aszTokens[0]
         if firstToken == '#' or firstToken == '':
             continue
         secondToken = aszTokens[1]
         if firstToken == "test":
             g_bTest = int(secondToken)
         elif firstToken == "run_tests":
             g_bRunTests = int(secondToken)
         elif firstToken == "lock_mutex":
             g_bLockMutex = int(secondToken)
         elif firstToken == "build":
             g_bBuild = int(secondToken)
         elif firstToken == "debug":
             g_bDebug = int(secondToken)
         elif firstToken == "dev":
             g_bDev = int(secondToken)
         elif firstToken == "shaders":
             g_bShaders = int(secondToken)
         elif firstToken == "sync":
             g_bSync = int(secondToken)
         elif firstToken == "email_alias":
             g_szEmailAlias = secondToken
         elif firstToken == "admin_email":
             g_szAdministrator = secondToken
         elif firstToken == "mail_host":
             g_szMailhost = secondToken
         elif firstToken == "sender_email":
             g_szSenderEmail = secondToken
         elif firstToken == "build_exe":
             g_szBuildExe = secondToken
         elif firstToken == "build_type":
             g_szBuildType = secondToken
         elif firstToken == "build_flags":
             g_szBuildFlags = secondToken
         elif firstToken == "test_file":
             g_szTestingFile = secondToken
         elif firstToken == "log_file":
             g_szLocalLogFile = secondToken
         elif firstToken == "error_dir":
             g_szPublishedErrorsDir = secondToken
         elif firstToken == "test_dir":
             g_szTestDirectory = secondToken
         elif firstToken == "src_files":
             g_szP4SrcFilesToWatch += secondToken 
         elif firstToken == "force_files":
             g_szP4ForceFilesToWatch += secondToken + ";"
         elif firstToken == "sync_files":
             g_szP4SyncFilesToWatch += secondToken + ";"
         elif firstToken == "mutex":
             g_szP4Mutex = secondToken
         elif firstToken == "change_counter":
             g_szP4ChangeCounter = secondToken
         elif firstToken == "verify_counter":
             g_szP4VerifiedCounter = secondToken
         elif firstToken == "build_name":
             g_szBuildName = secondToken
     PrintConfig()

#-----------------------------------------------------------------------------
# Main 
#-----------------------------------------------------------------------------
if __name__ == '__main__':
    try:
                print "----------------------------------------------------"
                print g_szBuildName + " BUILD SCRIPT STARTED"
                ParseConfigFile(sys.argv[1])

                while 1:
                    if (g_bDev | P4.AnyNewCheckins( g_szP4ChangeCounter, g_szP4SrcFilesToWatch )):
                        print "Changes Detected.\n"
                        if ( (g_bTest & ~g_bLockMutex) | ~g_bLockMutex | P4.Query(g_szP4Mutex) ):
                            PerformDailyBuild()
                            g_bDev = 0
                            print ""
                            print "------------------------------------------"
                            print "waiting for changes to be detected..."
                        else:
                            time.sleep( g_nSleepTimeBetweenChecks - ( g_bTest * g_nSleepTimeBetweenChecks ))
                    else:
                        time.sleep( g_nSleepTimeBetweenChecks )
    except RuntimeError, e:
        ComplainToAdmin(e)