global PPCPROJECTFAILED
global CVSSESSIONFILE
global CVSSESSIONNAME
global BUILDLIST
global BUILDNAME
global FOLDERTIME
global SOURCEPATH
global CHECKOUTTIME
global TINDERBOXTREE
global MACHINEADMIN
global CLOBBERBUILD
global STARTUPDISK
global BINARYPATH
property CONFIGFILENAME : "Tinderbox Config"

-------------------------
on CreateLogFile(logFilePath)
	-- creates a "build log" file
	-------------------------
	global logFileID
	set logFileID to 0
	try
		set logFileID to open for access file logFilePath with write permission
		--set the type of alias logFilePath to "CWIE"
	on error errMsg2 number errNum
		if (errNum is not -49) then
			-- nothing to do about such errors
			error errMsg2 & " error = " & errNum
		end if
	end try
end CreateLogFile

-------------------------
on CloseLogFile()
	-- close log file
	-------------------------
	global logFileID
	if logFileID is not 0 then close access logFileID
end CloseLogFile

-------------------------
on logMessage(m)
	-- logs a message to the log file
	-------------------------
	global logFileID
	try
		if logFileID is not 0 then write (m as string) & (ASCII character 13) to logFileID
		--display dialog m
	on error
		-- hmmm??
	end try
end logMessage

-------------------------
on trim(q)
	-- strip leading and trailing white space.
	-------------------------
	set realLast to length of q
	repeat
		if realLast is 0 then exit repeat
		set c to character realLast of q
		if c is not space and c is not tab then exit repeat
		set realLast to realLast - 1
	end repeat
	if (realLast is 0) then return ""
	set realFirst to 1
	repeat
		if realFirst > realLast then exit repeat
		set c to character realFirst of q
		if c is not space and c is not tab then exit repeat
		set realFirst to realFirst + 1
	end repeat
	if (realFirst > realLast) then return ""
	return text realFirst through realLast of q
end trim

-------------------------
on Token(aString, tokenRequested)
	-- Return the string up to the first space.  Input must be trimmed.
	-------------------------
	set tokenFirst to 0 -- offset of first char of current token
	set tokenCount to 0 -- which token we are examining
	set tokenScan to 0 -- offset of next character being examined
	set stringLength to length of aString
	if (tokenRequested = 0) then return ""
	repeat
		set tokenFirst to tokenFirst + 1
		set tokenCount to tokenCount + 1
		-- check for inital whitespace
		repeat
			set c to character tokenFirst of aString
			if c is not space and c is not tab then exit repeat
			set tokenFirst to tokenFirst + 1
		end repeat
		-- check for inital quote
		set isQuotedString to false
		if c is "\"" then
			set isQuotedString to true
			set tokenFirst to tokenFirst + 1
		end if
		-- scan for the next white space or the end of string
		set tokenScan to tokenFirst - 1
		repeat
			set tokenScan to tokenScan + 1
			if tokenScan > stringLength then exit repeat
			set c to character tokenScan of aString
			if (isQuotedString) then
				if c is "\"" then exit repeat
			else
				if c is space or c is tab then exit repeat -- whitespace
			end if
		end repeat
		-- Got a token
		if (tokenCount = tokenRequested) then
			return trim(text tokenFirst through (tokenScan - 1) of aString)
		end if
		if (tokenScan > stringLength) then return ""
		set tokenFirst to tokenScan
	end repeat
end Token

-------------------------
on monthNumber(dateObj)
	-------------------------
	set monthList to {January, February, March, April, May, 
		June, July, August, September, October, November, December}
	set m to month of dateObj
	repeat with i from 1 to number of items of monthList
		if m = item i of monthList then
			return i
		end if
	end repeat
end monthNumber

-------------------------
on ExtractTimeString(d)
	-- given a date object, extract hh.mm
	-------------------------
	set tstring to ""
	set t to (time of d as integer)
	set m to (t div 60 as integer)
	set h to m div 60
	set m to (m - h * 60)
	if (h < 10) then set tstring to tstring & "0"
	set tstring to tstring & h
	if (m < 10) then set tstring to tstring & "0"
	set tstring to tstring & m
	return tstring
end ExtractTimeString

-------------------------
on ExtractDateString(d)
	-- given a date object, extract yy/mm/dd
	-------------------------
	set dstring to (year of d) & "."
	set m to monthNumber(d)
	if (m < 10) then set dstring to dstring & "0"
	set dstring to dstring & m & "."
	set d to (day of d)
	if (d < 10) then set dstring to dstring & "0"
	set dstring to dstring & d
	return dstring
end ExtractDateString

-------------------------
on GetCVSTimeStamp(d)
	-- turns AppleScript date object into a text CVS type time stamp
	-- suitable for passing into MacCVS
	-------------------------
	set dstring to ""
	set m to monthNumber(d)
	if (m < 10) then set dstring to "0"
	set dstring to m & "/"
	set dayNum to (day of d)
	if (dayNum < 10) then set dstring to dstring & "0"
	set dstring to dstring & dayNum & "/"
	set theYear to ((year of d) as string)
	set dstring to dstring & {characters 3 thru 4 of theYear}
	
	set tstring to ""
	set t to (time of d as integer)
	set m to (t div 60 as integer)
	set h to m div 60
	set m to (m - h * 60)
	if (h < 10) then set tstring to tstring & "0"
	set tstring to tstring & h & ":"
	if (m < 10) then set tstring to tstring & "0"
	set tstring to tstring & m & ":"
	set s to (t mod 60)
	if (s < 10) then set tstring to tstring & 0
	set tstring to tstring & s
	
	set theresult to ((dstring & " " & tstring & " PST") as string)
	return theresult
end GetCVSTimeStamp

-------------------------
on GetTimeStamp()
	-- returns a numeric string given for a date object
	-------------------------
	set d to current date
	set dstring to ExtractDateString(d) & "." & ExtractTimeString(d)
	set dstring to dstring as string
	return dstring
end GetTimeStamp

-------------------------
-- given a full path, return the name of the last object
on GetCVSSessionName(path)
	-- given a mac style path, return the string name of the
	-- last object in a path
	-------------------------
	set going to true
	set i to the length of path
	
	repeat while going is true
		set theChar to character i of path
		if theChar is ":" then
			set going to false
		end if
		set i to i - 1
	end repeat
	set sessionName to (characters {i + 2} thru length of path)
	return sessionName as string
end GetCVSSessionName

-------------------------
on doOnePPCProject(projpath, targetName)
	-- process one PPC project, given path to project and the target
	-- if target is "" build frontmost target (the default)
	-------------------------
	global gDoAliases
	set noError to false
	with timeout of 3600 seconds (* one hour*)
		
		try
			tell application "CodeWarrior IDE 3.2"
				open {projpath}
			end tell
		on error errMsg number errNum
			logMessage("Error  : Can't open project " & projpath)
			set PPCPROJECTFAILED to true
			error
		end try
		
		if targetName is not "" then
			tell application "CodeWarrior IDE 3.2"
				Set Current Target (targetName as string)
			end tell
		end if
		
		try
			tell application "CodeWarrior IDE 3.2"
				set myErrors to Make Project
			end tell
		on error number errNum
			if errNum < 0 then
				set noError to true
			end if
		end try
		if noError is false then
			tell application "CodeWarrior IDE 3.2"
				set theProjectName to my GetProjectName(projpath)
				Save Error Window As {STARTUPDISK & ":" & FOLDERTIME & ":" & theProjectName & ".txt"}
			end tell
			set PPCPROJECTFAILED to true
		end if
		tell application "CodeWarrior IDE 3.2"
			Close Project
		end tell
	end timeout
	return (noError)
end doOnePPCProject


-------------------------
on ReadLn(fileRefNum)
	-- strip empty lines and strip terminal comments
	-------------------------
	try
		repeat
			set q to read fileRefNum before return
			if q is not "" then
				set hashOffset to offset of "#" in q
				if (hashOffset is 0) then set hashOffset to offset of "//" in q
				if (hashOffset > 1) then
					set q to text 1 thru (hashOffset - 1) of q
				end if
				if hashOffset is not 1 and q is not "" then
					-- strip trailing white space.
					set q to trim(q)
					if q is not "" then return q
				end if
			end if
		end repeat
	on error errString number errNum
		if errNum is -39 then -- no more lines (eof)
			return ""
		end if
		ErrorMessage(errString)
		error errString
		return errString
	end try
end ReadLn

on ProcessList(theListFile)
	try
		with timeout of 14400 seconds -- four hours
			tell application "MacPerl"
				Do Script {theListFile as alias}
			end tell
		end timeout
	on error
	end try
	
end ProcessList

(* Initialize script.  Read Config file. Get source directory *)
on Initialize()
	
	try
		-- find the config file at the same level as this applescript
		-- if you're running this in script debugger, uncomment lines below
		set oldDelimiters to AppleScript's text item delimiters
		set myPath to (path to me) as text
		set AppleScript's text item delimiters to {":"}
		set pl to (every text item of myPath)
		set configPath to (items 1 thru ((count of pl) - 1) of pl) as string
		set configPath to configPath & ":" & CONFIGFILENAME
		set AppleScript's text item delimiters to oldDelimiters
		
		set f to 0
		
		-- DEBUG		
		-- uncomment this for debugging		
		-- set configPath to "Forge:Tinderbox:cm:client:mac:contbuild:Tinderbox Config"
		set f to open for access {configPath as alias}
		
		set MACHINEADMIN to ReadLn(f)
		set TINDERBOXTREE to ReadLn(f)
		set BUILDNAME to ReadLn(f)
		
		set clobberstring to ReadLn(f)
		if clobberstring is "Clobber" then
			set CLOBBERBUILD to true
		else
			set CLOBBERBUILD to false
		end if
		
		set CVSSESSIONFILE to ReadLn(f)
		set BUILDLIST to ReadLn(f)
		set BINARYPATH to ReadLn(f)
		
		if f is not 0 then
			close access f
		end if
	on error
		display dialog "Failed to read config file!"
	end try
	
	set CHECKOUTTIME to GetCVSTimeStamp(current date)
	
	set SOURCEPATH to getLocalCVSRoot(CVSSESSIONFILE)
	
	tell application "Finder"
		set STARTUPDISK to name of startup disk
	end tell
	
	createLogDirectory(STARTUPDISK & ":")
	CreateLogFile(STARTUPDISK & ":" & FOLDERTIME & ":" & "Tinderbox Script Log")
	
	logMessage("Machine Administrator: " & MACHINEADMIN)
	logMessage("Building to Tinderbox Tree: " & TINDERBOXTREE)
	logMessage("Building: " & BUILDNAME)
	
	if (CLOBBERBUILD) then
		logMessage("Building Clobber")
	else
		logMessage("Building Depend")
	end if
	logMessage("CVS Session file at: " & CVSSESSIONFILE)
	logMessage("BuildList at: " & BUILDLIST)
	logMessage("Binary file at: " & BINARYPATH)
	return true
	
end Initialize

-- given a full path, return the name of the last object
on GetProjectName(path)
	set going to true
	set i to the length of path
	repeat while going is true
		set theChar to character i of path
		if theChar is ":" then
			set going to false
		end if
		set i to i - 1
	end repeat
	return (characters {i + 2} thru length of path)
end GetProjectName


on notifyHook(result)
	if result is true then
		tell application "Eudora Pro 3.1"
			activate
			set newMsg to make new message at end of mailbox "Out" of mail folder ""
			set field "Subject:" of newMsg to "Mac Continous Build FAILED"
			set body of newMsg to "
The last Mac Continous Build Failed at: " & " " & {CHECKOUTTIME as text} & "
Since you are on the hook, you might be responsible for the breakage. 

Please check the logs at: http://warp/tinderbox/showbuilds.cgi?tree=" & TINDERBOXTREE & "

to see if you busted the build."
			set field "To:" of newMsg to "bonsai-hook"
			activate
			queue message newMsg
			connect with sending without checking
		end tell
	end if
end notifyHook

on notifyTinderBoxStart()
	try
		tell application "Eudora Pro 3.1"
			activate
			set newMsg to make new message at end of mailbox "Out" of mail folder ""
			set body of newMsg to "
		
tinderbox: tree: " & TINDERBOXTREE & "
tinderbox: builddate: " & CHECKOUTTIME & "
tinderbox: status: building
tinderbox: build: " & BUILDNAME & " 
tinderbox: errorparser: mac
"
			set field "To:" of newMsg to "tinderbox-daemon@warp"
			activate
			queue message newMsg
			connect with sending without checking
			quit
		end tell
	on error
		CloseLogFile()
		display dialog "MAIL SERVER ERROR: CAN'T SEND COMPLETION E-MAIL. SCRIPT HALTED"
		error number -128
	end try
	
end notifyTinderBoxStart

on notifyTinderBox(result)
	try
		if result is true then
			
			tell application "Finder"
				set thelist to (every file of folder (STARTUPDISK & ":" & FOLDERTIME & ":"))
			end tell
			
			set numFiles to count of thelist
			set i to 1
			set aliasList to {}
			
			repeat numFiles times
				set aliasList to aliasList & (item i of thelist as alias)
				set i to i + 1
			end repeat
			
			tell application "Eudora Pro 3.1"
				activate
				set newMsg to make new message at end of mailbox "Out" of mail folder ""
				attach to newMsg documents aliasList
				set field "Subject:" of newMsg to "Mac Continous Build FAILED"
				set body of newMsg to "

tinderbox: tree: " & TINDERBOXTREE & "
tinderbox: builddate: " & CHECKOUTTIME & "
tinderbox: status: busted
tinderbox: build: " & BUILDNAME & " 
tinderbox: errorparser: mac
"
				set field "To:" of newMsg to "tinderbox-daemon@warp"
				activate
				queue message newMsg
				connect with sending without checking
				quit
			end tell
		end if
		
		if result is false then
			
			tell application "Finder"
				set thelist to (every file of folder (STARTUPDISK & ":" & FOLDERTIME & ":"))
			end tell
			
			set numFiles to count of thelist
			set i to 1
			set aliasList to {}
			
			repeat numFiles times
				set aliasList to aliasList & (item i of thelist as alias)
				set i to i + 1
			end repeat
			
			tell application "Eudora Pro 3.1"
				activate
				set newMsg to make new message at end of mailbox "Out" of mail folder ""
				set field "Subject:" of newMsg to "Mac Continous Build SUCCEEDED"
				set body of newMsg to "

tinderbox: tree: " & TINDERBOXTREE & "
tinderbox: builddate: " & CHECKOUTTIME & "
tinderbox: status: success
tinderbox: build: " & BUILDNAME & " 
tinderbox: errorparser: mac
"
				set field "To:" of newMsg to "tinderbox-daemon@warp"
				activate
				queue message newMsg
				connect with sending without checking
				quit
			end tell
		end if
	on error
		CloseLogFile()
		display dialog "MAIL SERVER ERROR: CAN'T SEND COMPLETION E-MAIL. SCRIPT HALTED"
		error number -128
	end try
	
end notifyTinderBox

on PageAdmin(pageMesg)
	tell application "Eudora Pro 3.1"
		activate
		set newMsg to make new message at end of mailbox "Out" of mail folder ""
		set field "To:" of newMsg to MACHINEADMIN
		set field "Subject:" of newMsg to TINDERBOXTREE & " " & BUILDNAME & " Continous Build failed"
		if pageMesg is not "" then
			set body of newMsg to pageMesg
		end if
		activate
		queue message newMsg
		connect with sending without checking
		quit
	end tell
end PageAdmin

-- remove all directories from the local root, removing the old tree
on removeOldTree()
	
	try
		tell application "Finder"
			open folder (SOURCEPATH as string)
			select every item of item of container window of folder (SOURCEPATH as string)
			delete selection
			close container window of folder (SOURCEPATH as string)
			empty trash
		end tell
	on error
		CloseLogFile()
		set PPCPROJECTFAILED to true
		logMessage("Error  : Couldn't delete the old tree.")
		PageAdmin("Couldn't nuke old tree!")
		display dialog ("Couldn't nuke old tree! SCRIPT HALTED")
		error number -128
	end try
	
end removeOldTree

-- remove the 'dist' directory from the local root
on removeOldDist() --
	
	try
		tell application "Finder"
			set distPath to SOURCEPATH & ":mozilla:dist"
			open folder (distPath as string)
			select every item of item of container window of folder (distPath as string)
			delete selection
			close container window of folder (distPath as string)
			empty trash
		end tell
	on error
		CloseLogFile()
		set PPCPROJECTFAILED to true
		logMessage("Error  : Couldn't delete the old 'dist' directory.")
		PageAdmin("Couldn't nuke old 'dist' directory!")
		display dialog ("Couldn't nuke old 'dist' directory! SCRIPT HALTED")
		error number -128
	end try
	
end removeOldDist

on checkoutModule(cvsmodule, cvsRevision)
	
	try
		with timeout of 1800 seconds (* 30 minutes *)
			if cvsRevision is "" then
				tell application "MacCVS Pro 2.1b2r1 PPC"
					tell session CVSSESSIONNAME
						activate
						checkout module cvsmodule
					end tell
				end tell
			else
				tell application "MacCVS Pro 2.1b2r1 PPC"
					tell session CVSSESSIONNAME
						activate
						checkout module cvsmodule revision cvsRevision
					end tell
				end tell
			end if
		end timeout
		if cvsRevision is "" then
			logMessage("Check-out of " & cvsmodule & "successful!")
		else
			logMessage("Check-out of " & cvsmodule & "at revision: " & cvsRevision & "  successful!")
		end if
	on error errMsg number errNum
		if cvsRevision is "" then
			set PPCPROJECTFAILED to true
			logMessage("Error  : Can't check out module: " & cvsmodule & " revision: Tip")
			logMessage("Error  : " & errMsg)
			error "Can't check out module"
		else
			set PPCPROJECTFAILED to true
			logMessage("Error  : Can't check out module: " & cvsmodule & " revision: " & cvsRevision)
			logMessage("Error  : " & errMsg)
			error "Can't check out module"
		end if
	end try
	
end checkoutModule

on checkOutTree(pathToCVSSession)
	
	try
		set oldDelimiters to AppleScript's text item delimiters
		set myPath to (path to me) as text
		set AppleScript's text item delimiters to {":"}
		set pl to (every text item of myPath)
		set configPath to (items 1 thru ((count of pl) - 1) of pl) as string
		set configPath to configPath & ":" & CONFIGFILENAME
		set AppleScript's text item delimiters to oldDelimiters
		
		set f to 0
		
		-- set configPath to "Forge:Tinderbox:cm:client:mac:contbuild:Tinderbox Config"
		set f to open for access {configPath as alias}
		(* read past the cruft of the config file *)
		ReadLn(f)
		ReadLn(f)
		ReadLn(f)
		ReadLn(f)
		ReadLn(f)
		ReadLn(f)
		ReadLn(f)
	on error
		CloseLogFile()
		set PPCPROJECTFAILED to true
		logMessage("Couldn't check-out the tree: couldn't read modules" & return)
		logMessage(errMsg)
		PageAdmin("Couldn't check-out the tree: couldn't read modules")
		display dialog "Couldn't read modules from config file: SCRIPT HALTED"
		error number -128
	end try
	
	-- open the session	
	tell application "MacCVS Pro 2.1b2r1 PPC"
		activate
		open {pathToCVSSession as alias}
	end tell
	
	try
		repeat
			set cvsmodule to ReadLn(f)
			if cvsmodule is "" then exit repeat
			if cvsmodule = "CVS Session" then
				switchCVSSession(f)
			else
				set revision to Token(cvsmodule, 2)
				set cvsmodule to Token(cvsmodule, 1)
				checkoutModule(cvsmodule, revision)
			end if
		end repeat
		logMessage("### Checked-out all required modules")
	on error
		--tell application "MacCVS Pro 2.1b2r1 PPC"
		--quit
		--end tell
		-- toss something up to run()
		error
	end try
	
	--tell application "MacCVS Pro 2.1b2r1 PPC"
	--quit
	--end tell
	
end checkOutTree

on switchCVSSession(f)
	
	tell application "MacCVS Pro 2.1b2r1 PPC"
		close session CVSSESSIONNAME
	end tell
	
	set newSessionPath to ReadLn(f)
	set newSessionName to GetCVSSessionName(newSessionPath)
	
	set CVSSESSIONNAME to newSessionName
	
	tell application "MacCVS Pro 2.1b2r1 PPC"
		activate
		open {newSessionPath as alias}
	end tell
	
end switchCVSSession

on getLocalCVSRoot(pathToCVSSession)
	
	set CVSSESSIONNAME to GetCVSSessionName(pathToCVSSession)
	try
		tell application "MacCVS Pro 2.1b2r1 PPC"
			open {pathToCVSSession as alias}
			tell session CVSSESSIONNAME
				set theLocalRoot to local root
			end tell
			--quit
		end tell
		set pathLength to {(get the length of (theLocalRoot as text)) as text}
		return (characters 1 thru {pathLength - 1} of (theLocalRoot as text))
	on error
		display dialog "Error  : CVS Session path wrong, or doesn't have a local root. SCRIPT HALTED!"
		error number -128
	end try
	
end getLocalCVSRoot

on createLogDirectory(destFolder)
	
	tell application "Finder"
		set FOLDERTIME to my GetTimeStamp()
		make new folder at folder (destFolder)
		set the name of the result to FOLDERTIME
	end tell
	
end createLogDirectory

on mozillaLives(filePath)
	
	try
		tell application "Finder"
			set fileTest to file filePath
			set creatorType to get the creator type of fileTest
			set fileTest to application file filePath
			set partitionSize to get the partition size of fileTest
			if (creatorType is "????" and partitionSize > 0) then
				return false
			else
				return true
			end if
		end tell
	on error
		return true
	end try
	
end mozillaLives

on copyFile(srcPath, destFolder)
	
	try
		tell application "Finder"
			copy file (srcPath as alias) to folder (destFolder as alias)
			-- set the name of the result to destName
		end tell
	on error msgErr
		logMessage("Error: Could not copy - " & srcPath & msgErr)
	end try
	
end copyFile

on getDriveName(path)
	
	set going to true
	set i to 1
	
	repeat while going is true
		set theChar to character i of path
		if theChar is ":" then
			set going to false
		end if
		set i to i + 1
	end repeat
	set driveName to (characters 1 thru {i - 1} of path)
	return driveName as string
	
end getDriveName

on trashItem(filePath)
	
	try
		tell application "Finder"
			select item (filePath as string)
			delete selection
			empty trash
		end tell
	on error
		logMessage("Error  : Couldn't delete the old binary.")
		PageAdmin("Couldn't nuke old binary!")
		display dialog "Couldn't nuke old binary: SCRIPT HALTED"
		error number -128
	end try
	
end trashItem

on run
	
	-- if we are executing first time, skip time delay check	
	set firstRun to true
	
	--repeat
	
	-- if we're cycling around, make sure we don't cycle faster than 10 minutes		
	if firstRun is false then
		if endTime - startTime is less than 600 then
			repeat while endTime - startTime is less than 600
				set endTime to current date
			end repeat
		end if
	end if
	
	set firstRun to false
	
	set startTime to current date
	
	-- 2 hour timeout		
	with timeout of 7200 seconds
		
		set PPCPROJECTFAILED to false
		
		-- read config file and create log directories			
		Initialize()
		
		notifyTinderBoxStart()
		
		if (CLOBBERBUILD) then
			removeOldTree()
		else
			removeOldDist() --
		end if
		
		(*
			else
				trashItem("")
			end
			*)
		
		try
			checkOutTree(CVSSESSIONFILE)
		on error
			PageAdmin("Can't check out tree!")
		end try
		
		if PPCPROJECTFAILED is false then
			set SOURCEPATH to getLocalCVSRoot(CVSSESSIONFILE)
			set SOURCEPATH to SOURCEPATH as string
			
			ProcessList(SOURCEPATH & BUILDLIST)
			
			tell application "CodeWarrior IDE 3.2"
				quit
			end tell
			
			tell application "MacCVS Pro 2.1b2r1 PPC"
				quit
			end tell
			
			tell application "ToolServer"
				quit
			end tell
			
		end if
		
		-- set builddrive to getDriveName(SOURCEPATH)
		
		copyFile(SOURCEPATH & ":Mozilla.BuildLog", STARTUPDISK & ":" & FOLDERTIME & ":")
		set PPCPROJECTFAILED to mozillaLives(SOURCEPATH & BINARYPATH)
		
		CloseLogFile()
		
		try
			notifyTinderBox(PPCPROJECTFAILED)
		on error
			PageAdmin("Couldn't send Tinderbox completion message!")
		end try
		
		tell application "Finder"
			select folder FOLDERTIME of startup disk
			delete selection
			--empty trash
			restart --
		end tell
		
	end timeout
	
	set endTime to current date
	
	--end repeat
	
end run
