// // task.js // // Patrick Franklin 9/27/99 // // DESCRIPTION: // This function is called to perform the tasks of actually sync'ing // and building. // Include('types.js'); Include('utils.js'); Include('staticstrings.js'); Include('slavetask.js'); Include('robocopy.js'); // File System Object var g_FSObj;// = new ActiveXObject("Scripting.FileSystemObject"); var g_fAbort = false; var g_ErrLog = null; var g_SyncLog = null; var g_MachineName = LocalMachine; var g_RetrySD = false; var g_strDelayedExitMessage = null; var g_fSDClientError = false; // When set we need to do special processing of the SD output var g_hTasks; // Global flags for syncing and parsing results var g_strTaskFlag; var g_strStepFlag; var g_strExitFlag; var g_strScriptSyncFlag; // Internal to this script var g_fIsBetweenSteps; var g_fIsRoot; var g_fIsMerged; var g_strMergeFile=''; var g_cErrors = 0; // Used to track error count during file copy operations. var g_Depot; var g_strDepotName; var g_Task; var g_DepotIdx; var g_TaskIdx; var g_fAbort = false; var g_robocopy; var g_pid; // The PID of the currently running process. var g_aMergedPos = [0, 0]; // Global Definitions var g_reSDRetryText = new RegExp(": (WSAECONNREFUSED|WSAETIMEDOUT)"); var RESOLVE = 'resolve'; var REVERT = 'revert'; var PASS0 = 'pass0'; var COMPILE = 'compile'; var LINK = 'link'; var STATUS = 'status'; var EXITED = 'exited'; var SYNC_STATUS_UPDATE_INTERVAL = 3000; // Check once every 3 seconds for updated sync status var WAIT_FOR_PROCESS_EXIT_TIME = 5 * 1000; // During abort, wait this long for a process to terminate // Constants for controlling SD.exe command retry operations. var INITIAL_RETRY_DELAY = (10 * 1000) // Wait 10 seconds before we retry var MAX_RETRY_DELAY = (5 * 60 * 1000) // Wait at most 5 minutes between SD attempts var MAX_TOTAL_RETRY_TIME = (30 * 60 * 1000) // Wait no more than 30 minutes for SD to succeed var strRetryNow = "DoubleClickToRetryNow"; /************************************************************************* * * Functions * *************************************************************************/ function task_js::OnScriptError(strFile, nLine, nChar, strText, sCode, strSource, strDescription) { var vRet = 1; var strDepotName = ''; try { SignalThreadSync(g_strAbortTask); // Tell 'task.js' to exit ASAP SignalThreadSync(g_strExitFlag); // Play dead -- lie to slave.js and say we terminated. if (g_strDepotName) strDepotName = g_strDepotName; vRet = CommonOnScriptError("task_js(" + LocalMachine + "," + g_strDepotName + ")", strFile, nLine, nChar, strText, sCode, strSource, strDescription); // Tell all our tasks to terminate // Once this function returns, strScript.js will stop executing. g_fAbort = true; } catch(ex) { } if (g_pid) { TerminateProcess(g_pid); g_pid = null; } return vRet; } function SPLogMsg(msg) { LogMsg('(' + g_strDepotName + ') ' + msg, 1); } function SPSignalThreadSync(signal) { LogMsg('(' + g_strDepotName + ') Signalling ' + signal, 1); SignalThreadSync(signal); } // // ScriptMain() // // DESCRIPTION: // This script is called to perform the operation in the supplied task. // // RETURNS: // none // function task_js::ScriptMain() { var ii; var aParams; var strSyncFlag; var strSDRoot; var iRet; var strBuildType; var strBuildPlatform; try { InitTasks(); // Parse input Parameter List aParams = ScriptParam.split(','); g_strTaskFlag = aParams[0]; // Sync Flag g_strStepFlag = aParams[1]; // Step Flag g_strExitFlag = aParams[2]; // Exit Flag strSDRoot = aParams[3]; // SD Root g_DepotIdx = aParams[4]; // Depot Index g_TaskIdx = aParams[5]; // Task Index // Signal that the Task is started SignalThreadSync(g_strTaskFlag); CommonVersionCheck(/* $DROPVERSION: */ "V(########) F(!!!!!!!!!!!!!!)" /* $ */); g_strTaskFlag += ',AStepFinished'; // Get global handles the Depot and Task g_Depot = PublicData.aBuild[0].aDepot[g_DepotIdx]; // Grab a local copy of the depot name for OnScriptError // because if slave_js has crashed, OnScriptError will be // unable to deference g_Depot. g_strDepotName = g_Depot.strName; g_Task = PublicData.aBuild[0].aDepot[g_DepotIdx].aTask[g_TaskIdx] // Initialize error counts and mark the task as in progress g_Task.strStatus = NOTSTARTED; g_Task.cFiles = 0; g_Task.cResolved = 0; g_Task.cErrors = 0; g_Task.cWarnings = 0; g_Task.fSuccess = true; g_fIsRoot = g_Depot.strName.toLowerCase() == g_strRootDepotName; g_fIsMerged = g_Depot.strName.toLowerCase() == g_strMergedDepotName; g_FSObj = new ActiveXObject("Scripting.FileSystemObject"); // Parse input Parameter List SPLogMsg(g_Task.strName + ' task thread for ' + g_Depot.strName + ' launched & waiting.'); // Wait until instructed to step iRet = WaitAndAbort(g_strStepFlag, 0, null); SignalThreadSync(g_strStepAck); if (iRet == 0) ThrowError("Task abort on start", strSDRoot); SPLogMsg(g_Task.strName + ' task thread for ' + g_Depot.strName + ' started.'); ResetSync(g_strStepFlag); g_Task.strStatus = INPROGRESS; g_Task.dateStart = new Date().toUTCString(); FireUpdateEvent(); JAssert(g_Depot.strName.toLowerCase() == g_strRootDepotName || (g_Depot.strPath.toLowerCase() == (strSDRoot.toLowerCase() + '\\' + g_Depot.strName.toLowerCase())), 'Mismatched depot and path! (' + g_Depot.strPath + ', ' + strSDRoot + '\\' + g_Depot.strName + ')'); // Check to see what task is to be executed and go g_strScriptSyncFlag = g_Depot.strPath + 'InternalTaskFinished'; ResetSync(g_strScriptSyncFlag); if (g_hTasks[g_Task.strName]) { if (!g_hTasks[g_Task.strName](strSDRoot)) { OnError('Error during ' + g_Task.strName + '.', null, g_Task.cErrors != 0); } } } catch(ex) { SPLogMsg("task.js exception occurred : " + ex); OnError('task.js exception occurred : ' + ex); } SPLogMsg(g_Task.strName + ' for ' + g_Depot.strName + ' completed.'); // Mark the Task as complete. g_Task.strStatus = COMPLETED; g_Task.dateFinish = new Date().toUTCString(); if (g_Task.fSuccess) g_Depot.strStatus = WAITING; else { g_Depot.strStatus = ERROR; SPLogMsg("Set Depot Status to error 1"); } FireUpdateEvent(); try { if (g_ErrLog) g_ErrLog.Close(); if (g_SyncLog) g_SyncLog.Close(); } catch(ex) { } if (g_robocopy != null) g_robocopy.UnRegister(); // Signal that the Task is done and that a Step has finished SignalThreadSync(g_strTaskFlag + ',' + g_strExitFlag); } function InitTasks() { g_hTasks = new Object(); g_hTasks[SCORCH] = taskScorchFiles; g_hTasks[SYNC] = taskSyncFiles; g_hTasks[BUILD] = taskBuildFiles; g_hTasks[COPYFILES] = taskCopyFiles; g_hTasks[POSTBUILD] = taskPostBuild; } function task_js::OnEventSourceEvent(RemoteObj, DispID, cmd, params) { var objRet = new Object; objRet.rc = 0; try { if (g_robocopy == null || !g_robocopy.OnEventSource(objRet, arguments)) { } } catch(ex) { JAssert(false, "an error occurred in OnEventSourceEvent() \n" + ex); } return objRet.rc; } function DoRetryableSDCommand(strOperation, strStatus, objDepot, objTask, strTitle, strNewCmd) { var iRet; strTitle = strTitle + ' ' + objDepot.strName; var nTimeout = INITIAL_RETRY_DELAY ; var nTotalWaitTime = 0; var strMsg; do { if (g_RetrySD) { FireUpdateEvent(); ResetSync(g_strScriptSyncFlag); SPLogMsg("Waiting " + nTimeout / 1000 + " seconds before retry"); ResetSync(strRetryNow); WaitAndAbort(strRetryNow, nTimeout, null); nTotalWaitTime += nTimeout; nTimeout *= 2; // Double the delay if (nTimeout > MAX_RETRY_DELAY) nTimeout = MAX_RETRY_DELAY; } if (g_fAbort) break; objTask.strOperation = strOperation; objDepot.strStatus = strStatus; g_RetrySD = false; if (g_pid = MyRunLocalCommand(strNewCmd, objDepot.strPath, strTitle, true, true, false)) { objTask.strStatus = INPROGRESS; } else { g_ErrLog.WriteLine('Unable to execute command (' + GetLastRunLocalError() + '): ' + strNewCmd); return false; } SPLogMsg(strOperation + ' for ' + objDepot.strName + ' started. PID='+g_pid); iRet = WaitAndAbortParse(g_strScriptSyncFlag, g_pid, false); g_fSDClientError = false; } while (g_RetrySD && !g_fAbort && nTotalWaitTime < MAX_TOTAL_RETRY_TIME); if (g_Task.nRestarted > 0) { if (g_RetrySD || !g_Task.fSuccess) { strMsg = strOperation + " failed after retrying " + g_Task.nRestarted + " times"; g_ErrLog.Writeline(strMsg); OnError(strMsg); } else g_ErrLog.Writeline(strOperation + " completed successfully after retrying " + g_Task.nRestarted + " times"); } g_pid = null; return iRet; } // // taskScorch Files // // DESCRIPTION: // This routine handles the process of actually Scorch'ing the files. // Scorch'ing has two phases. // // 1. revert_public // 2. Scorch // // RETURNS: // true - if successful // false - if failure // // TODO: make a subroutine to handle errlog and Scorchlog function taskScorchFiles(strSDRoot) { var strNewCmd; var strParams = ''; var iRet; // Open the log files g_ErrLog = LogCreateTextFile(g_FSObj, g_Task.strErrLogPath, true); g_SyncLog = LogCreateTextFile(g_FSObj, g_Task.strLogPath, true); if (g_fIsRoot && !PrivateData.objConfig.Options.fIncremental) { // // revert public // if (PrivateData.objConfig.Options.RevertParams) strParams = PrivateData.objConfig.Options.RevertParams; strNewCmd = MakeRazzleCmd(strSDRoot) + ' && revert_public.cmd ' + strParams + ' 2>&1'; // Run the Command iRet = DoRetryableSDCommand(REVERT, SCORCHING, g_Depot, g_Task, "Revert_Public", strNewCmd); if (iRet == 0 || g_Task.fSuccess == false) return false; // // Scorch // ResetSync(g_strScriptSyncFlag); g_Task.strOperation = SCORCH; FireUpdateEvent(); if (PrivateData.objConfig.Options.ScorchParams) strParams = PrivateData.objConfig.Options.ScorchParams; else strParams = ''; // Construct Command strNewCmd = MakeRazzleCmd(strSDRoot) + ' && nmake -lf makefil0 scorch_source ' + strParams + ' 2>&1'; // Run the Command iRet = DoRetryableSDCommand(SCORCH, SCORCHING, g_Depot, g_Task, "Scorch", strNewCmd); if (iRet == 0 || g_Task.fSuccess == false) return false; } return true; } // // taskSyncFiles // // DESCRIPTION: // This routine handles the process of actually sync'ing the files. // Sync'ing has two phases. // // 1. Sync // 2. Resolve // // RETURNS: // true - if successful // false - if failure // // TODO: make a subroutine to handle errlog and synclog function taskSyncFiles(strSDRoot) { var strNewCmd; var strParams = ''; var iRet; // Open the log files g_ErrLog = LogCreateTextFile(g_FSObj, g_Task.strErrLogPath, true); g_SyncLog = LogCreateTextFile(g_FSObj, g_Task.strLogPath, true); // // Sync the files // if (PrivateData.objConfig.Options.SyncParams) strParams = PrivateData.objConfig.Options.SyncParams; // Construct the new Cmd strNewCmd = MakeRazzleCmd(strSDRoot) + ' && sd -s sync ' + strParams + ' 2>&1'; // Run the Command iRet = DoRetryableSDCommand(SYNC, SYNCING, g_Depot, g_Task, "Sync", strNewCmd); if (iRet == 0 || g_Task.fSuccess == false) return false; // // Resolve any merge conflicts // ResetSync(g_strScriptSyncFlag); g_Task.strOperation = RESOLVE; FireUpdateEvent(); if (PrivateData.objConfig.Options.ResolveParams) strParams = PrivateData.objConfig.Options.ResolveParams; else strParams = ''; // Construct Command strNewCmd = MakeRazzleCmd(strSDRoot) + ' && sd -s resolve -af ' + strParams + ' 2>&1'; // Run the Command iRet = DoRetryableSDCommand(RESOLVE, SYNCING, g_Depot, g_Task, "Resolve", strNewCmd); if (iRet == 0 || g_Task.fSuccess == false) return false; return true; } function WaitAndAbortParse(strScriptSyncFlag, pid, fPostBuild) { var nEvent; g_nSyncParsePosition = 0; do { nEvent = WaitAndAbort(strScriptSyncFlag, SYNC_STATUS_UPDATE_INTERVAL, pid); if (g_fAbort) return nEvent; if (nEvent == 0) HandleParseEvent(pid, "update", 0, fPostBuild); } while(nEvent == 0); // After SYNC exits, check to see if we have a delayed error message if (g_strDelayedExitMessage) { g_ErrLog.WriteLine('SD unexpectedly exited with one or more errors.'); g_ErrLog.Writeline(g_strDelayedExitMessage ); OnError(g_strDelayedExitMessage); } return nEvent; } // // taskBuildFiles // // DESCRIPTION: // This routine handles the process of actually buldinging the files. // building requires only a signal command but reports status after // each pass. // // When each pass completes the status should be updated and then // the process should continue. // // // RETURNS: // true - if successful // false - if failure // function taskBuildFiles(strSDRoot) { var nTracePoint = 0; try { var strNewCmd; var strTitle; var iSignal; var strParams = ''; var strDepotExcl = ''; var strLogOpt = "-j build_" + g_Depot.strName + " -jpath " + PrivateData.strLogDir; var strCleanBuild = "-c"; // // Build the files // g_Task.strOperation = PASS0; // Construct Command Elements strTitle = 'Build ' + g_Depot.strName; if (PrivateData.objConfig.Options.BuildParams) strParams = PrivateData.objConfig.Options.BuildParams; if (PrivateData.objConfig.Options.fIncremental) strCleanBuild = ""; if (g_fIsRoot) { strDepotExcl = '~' + PrivateData.aDepotList.join(' ~'); strLogOpt = "-j build_2" + g_Depot.strName + " -jpath " + PrivateData.strLogDir; } if (g_fIsMerged) strLogOpt = "-j build_merged" + " -jpath " + PrivateData.strLogDir; strNewCmd = MakeRazzleCmd(strSDRoot) + ' && build -3 ' + strCleanBuild + ' -p ' + strLogOpt + ' ' + strParams + ' ' + strDepotExcl + ' 2>&1'; g_Depot.strStatus = BUILDING; // Execute Command if (g_pid = MyRunLocalCommand(strNewCmd, g_Depot.strPath, strTitle, true, false, false)) { g_Task.strStatus = INPROGRESS; } else { AppendToFile(g_ErrLog, g_Task.strErrLogPath, 'Unable to execute command (' + GetLastRunLocalError() + '): ' + strNewCmd); return false; } SPLogMsg('Build for ' + g_Depot.strName + ' started. PID='+g_pid); FireUpdateEvent(); nTracePoint = 1; // Wait for Command to complete g_fIsBetweenSteps = false; do { nTracePoint = 2; iSignal = WaitAndAbort(g_strScriptSyncFlag + ',' + g_strStepFlag, 0, g_pid); if (g_fAbort) return false; nTracePoint = 3; if (iSignal == 2) { var iSend; // We've been told to move to the next pass in the build. // Indicate that we received the signal // BUGBUG Extra debug info messages //SPLogMsg("SignalThreadSync(" + g_strStepAck + ");"); SignalThreadSync(g_strStepAck); nTracePoint = 4; ResetSync(g_strStepFlag); JAssert(g_fIsBetweenSteps, 'Bug in slave.js or harness.js! Told to step when we werent waiting.'); g_Depot.strStatus = BUILDING; g_fIsBetweenSteps = false; // Continue on SPLogMsg("TASK STEPPING " + g_Depot.strName); iSend = SendToProcess(g_pid, 'resume', ''); nTracePoint = 5; if (iSend != 0) { nTracePoint = 5.5; SPLogMsg('SendToProcess on pid ' + g_pid + ' returned ' + iSend); } nTracePoint = 6; FireUpdateEvent(); } nTracePoint = 7; } while (iSignal == 2); g_pid = null; return true; } catch(ex) { SPLogMsg("exception caught, TracePoint = " + nTracePoint + " " + ex); throw ex; } } //+--------------------------------------------------------------------------- // // Function: taskCopyFiles // // Synopsis: Copy the log files and other stuff necessary for postbuild. // // Arguments: [strSDRoot] -- Enlistment root // //---------------------------------------------------------------------------- function taskCopyFiles(strSDRoot) { var strDestDir; var i; g_SyncLog = LogCreateTextFile(g_FSObj, g_Task.strLogPath, true); g_SyncLog.WriteLine("Copying log files"); SPLogMsg("taskCopyFiles"); if (PrivateData.objEnviron.BuildManager.PostBuildMachine == LocalMachine) { // Do nothing - this is the postbuild machine. /* for (i = 0; i < PrivateData.objEnviron.Machine.length; ++i) { if (PrivateData.objEnviron.Machine[i].Name == PrivateData.objEnviron.BuildManager.PostBuildMachine) { strDestDir = MakeUNCPath(PrivateData.objEnviron.BuildManager.PostBuildMachine, "Build_Logs", BUILDLOGS); CopyLogFiles(strDestDir, true); } } */ } else { // Copy my logfiles to the postbuild machine. for (i = 0; i < PrivateData.objEnviron.Machine.length; ++i) { if (PrivateData.objEnviron.Machine[i].Name == PrivateData.objEnviron.BuildManager.PostBuildMachine) { strDestDir = MakeUNCPath(PrivateData.objEnviron.BuildManager.PostBuildMachine, "Build_Logs", BUILDLOGS); CopyBinariesFiles(PrivateData.objEnviron.Machine[i].Enlistment); CopyLogFiles(strDestDir); } } } SPLogMsg("End of copyfiles"); return true; } //+--------------------------------------------------------------------------- // // Function: taskPostBuild // // Synopsis: Does post build processing to create a setup version of // the product. // // Arguments: [strSDRoot] -- Enlistment root // //---------------------------------------------------------------------------- function taskPostBuild(strSDRoot) { var iRet; var strNewCmd; var strTitle; var strParams = ''; if (PrivateData.fIsStandalone == true || PrivateData.objEnviron.BuildManager.PostBuildMachine == LocalMachine) { JAssert(g_fIsRoot, 'taskPostBuild called on a non-root depot!'); g_Task.strOperation = POSTBUILD; g_ErrLog = LogCreateTextFile(g_FSObj, g_Task.strErrLogPath, true); g_SyncLog = LogCreateTextFile(g_FSObj, g_Task.strLogPath, true); // Construct Command Elements strTitle = 'PostBuild'; if (PrivateData.objConfig.Options.PostbuildParams) strParams = PrivateData.objConfig.Options.PostbuildParams; strNewCmd = MakeRazzleCmd(strSDRoot); strNewCmd += ' & perl ' + strSDRoot + '\\Tools\\populatefromvbl.pl -verbose -checkbinplace -force'; strNewCmd += ' && postbuild.cmd ' + strParams + ' 2>&1'; g_Depot.strStatus = POSTBUILD; // Execute Command if (g_pid = MyRunLocalCommand(strNewCmd, g_Depot.strPath, strTitle, true, true, false)) { g_Task.strStatus = INPROGRESS; } else { g_ErrLog.WriteLine('Unable to execute command (' + GetLastRunLocalError() + '): ' + strNewCmd); return false; } SPLogMsg('Postbuild started. PID='+g_pid); FireUpdateEvent(); iRet = WaitAndAbortParse(g_strScriptSyncFlag, g_pid, true); FireUpdateEvent(); if (iRet == 0 || g_Task.fSuccess == false) return false; g_pid = null; } else { AppendToFile(g_SyncLog, g_Task.strLogPath, "postbuild not necessary on this machine."); } return true; } // // OnProcessEvent // // DESCRIPTION: // This routine is called when the command issues by RunLocalCommand // is completed // // RETURNS: // none // function task_js::OnProcessEvent(pid, evt, param) { try { switch (g_Task.strOperation) { case REVERT: case SCORCH: case SYNC: case RESOLVE: HandleParseEvent(pid, evt, param, false); SignalThreadSync(g_strScriptSyncFlag); break; case PASS0: case COMPILE: case LINK: if (ParseBuildMessages(pid, evt, param) == 'exited') { FireUpdateEvent(); SignalThreadSync(g_strScriptSyncFlag); } break; case POSTBUILD: HandleParseEvent(pid, evt, param, true); SignalThreadSync(g_strScriptSyncFlag); break; default: JAssert(false, "Unhandled task type in OnProcessEvent!"); break; } } catch(ex) { SPLogMsg("exception in OnProcessEvent pid=" + pid + ",evt=" + evt + ", " + ex); } } function HandleParseEvent(pid, evt, param, fPostBuild) { var strMsg; ParseSDResults(pid, GetProcessOutput(pid), fPostBuild); if (evt == 'crashed' || evt == 'exited' || evt == 'terminated') { if (evt != 'exited' || param != 0) // If Not normal exit { strMsg = ") terminated abnormally"; if (g_Task.cErrors == 0) strMsg += " (with no previously reported errors)"; AppendToFile(g_ErrLog, g_Task.strErrLogPath, g_Task.strName + " (" + g_Depot.strName + strMsg); if (param == null) param = 1; // Simplify the "if" below, and ensure that OnError() gets called. } } if (!g_RetrySD) { if ((evt == 'crashed' || evt == 'exited' || evt == 'terminated') && param != 0 && g_Task.fSuccess ) { if (evt != 'exited' || g_Task.cErrors == 0) // If Not (error exit, but errors already reported && ignored by user) OnError(g_Task.strName + ' for ' + g_Depot.strName + ' returned ' + param); } } FireUpdateEvent(); } // ParseSDResults() // // DESCRIPTION: // This routine parses the results from Source Depot or PostBuild // // RETURNS: // true - if successful // false - if unsuccessful // // NOTES: // // The source depot log format is // :
// where
often is of the format: // - // // The postbuild log format is //