|
|
//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1995
//
// File: autostart.js
//
// Contents: A script which will connect to a Build Manager machine and
// start a build. This can be used to start builds automatically
// with the task scheduler or by scripts.
//
//----------------------------------------------------------------------------
var RemoteObj = null;
var DEFAULT_IDENTITY_BM = "BuildManager"; var DEFAULT_IDENTITY_BUILDER = "Build"; var strBldMgr; var strBldMgrIdentity = DEFAULT_IDENTITY_BM; var strConfigURL = null; var strEnviroURL = null;
var strConfigXML; var strEnviroXML; var strTimeStamp;
var fForceRestart = false; var fCreateMachines = false; var fUpdateBinaries = false; var fSetTimeStamp = false; var fTryAgain = true;
var aMachines = new Array();
// Capture variables
var fCaptureLogsFromMachine; var fCaptureLogs; var strCaptureLogDir; var strCaptureLogMan; var strCaptureLogManIdentity; var g_FSObj;
var vRet = 0; //
// First, parse command line arguments
//
Error.prototype.toString = Error_ToString; ParseArguments(WScript.Arguments);
if (fUpdateBinaries) { vRet = DoBinariesUpdate(); } else if (fCaptureLogs) { g_FSObj = new ActiveXObject("Scripting.FileSystemObject"); // Parse input Parameter List
if (fCaptureLogsFromMachine) vRet = CaptureLogsManager(strCaptureLogDir, strCaptureLogMan, strCaptureLogManIdentity); else vRet = CaptureLogsEnviro(strCaptureLogDir); } else { vRet = AutoStart(); }
WScript.Quit(vRet);
function ParseArguments(Arguments) { var strArg; var chArg0; var chArg1; var argn;
for(argn = 0; argn < Arguments.length; argn++) { strArg = Arguments(argn); chArg0 = strArg.charAt(0); chArg1 = strArg.toLowerCase().slice(1);
if (chArg0 != '-' && chArg0 != '/') { if (!strConfigURL) strConfigURL = Arguments(argn); else if (!strEnviroURL ) strEnviroURL = Arguments(argn); else Usage(1); } else { switch(chArg1) { case 'f': fForceRestart = true; break; case 'u': fUpdateBinaries = true; fCreateMachines = true; break; case 't': fSetTimeStamp = true; argn++; if (argn < Arguments.length) strTimeStamp = Arguments(argn); else Usage(1); break; case 'log': fCaptureLogs = true; argn++; if (argn < Arguments.length) strCaptureLogDir = Arguments(argn); else Usage(2); fCreateMachines = true; break; case 'logman': fCaptureLogsFromMachine = true; argn++; if (argn < Arguments.length) strCaptureLogMan = Arguments(argn); else Usage(3); argn++; if (argn < Arguments.length) strCaptureLogManIdentity = Arguments(argn); else Usage(4); break; default: Usage(5); break; } } } // Insist that the config and enviro templates are supplied.
if (!fCaptureLogsFromMachine && (!strConfigURL || !strEnviroURL)) { if (!fCaptureLogs) { WScript.Echo('!fCaptureLogsFromMachine' + !fCaptureLogsFromMachine); WScript.Echo('!strConfigURL' + !strConfigURL); WScript.Echo('!strEnviroURL' + !strEnviroURL); Usage(6); } } if (fCaptureLogsFromMachine && !fCaptureLogs) Usage(7); }
function AutoStart() { try { var ret; var err = new Error(); var PublicData;
LoadEnvironmentTemplate(); LoadConfigTemplate();
try { var objOD = new ActiveXObject('MTScript.ObjectDaemon', strBldMgr) RemoteObj = objOD.OpenInterface(strBldMgrIdentity, 'MTScript.Remote', true); } catch(ex) { if (strBldMgr && strBldMgr.length > 0) err.description = 'Sorry. Could not connect to machine "' + strBldMgr + '\\' + strBldMgrIdentity + '"'; else err.description = 'Sorry. Could not connect to the local machine.';
err.description += '\n\tVerify that the machine is available and that mtscript.exe\n\tis running on the machine.';
err.details = ex.description;
throw(err); }
PublicData = RemoteObj.Exec('getpublic', 'root'); PublicData = eval(PublicData);
CommonVersionCheck("$DROPVERSION: V(2463.0 ) F(autostart.js )$", PublicData);
// Check the current mode of the machine. If it's idle, then switch it
// into master or standalone mode and get it going.
if (PublicData.strMode != 'idle') { if (fForceRestart) { WScript.Echo('The machine is not idle. Forcing a restart...');
ret = RemoteObj.Exec('setmode', 'idle');
WScript.Sleep(5000); } else { WScript.Echo('The machine is not idle. Use -f to force a restart.');
return 1; } }
while (!StartBuild() && fTryAgain) { ret = RemoteObj.Exec('setmode', 'idle');
WScript.Sleep(5000);
fTryAgain = false; }
WScript.Echo('Build started successfully.'); } catch(ex) { WScript.Echo('An error occurred starting the build:'); WScript.Echo('\t' + ex); WScript.Echo('');
return 1; } return 0; }
function MachineInfo() { this.strName = ''; this.strEnlistment = ''; this.fPostBuild = false; }
function StartBuild() { var ret = 'ok'; var err = new Error();
if (fSetTimeStamp) { ret = RemoteObj.Exec('setstringmap', "%today%=" + strTimeStamp); }
if (ret == 'ok') { ret = RemoteObj.Exec('setconfig', strConfigXML);
if (ret == 'ok') { ret = RemoteObj.Exec('setenv', strEnviroXML);
if (ret == 'ok') { ret = RemoteObj.Exec('setmode', 'master');
if (ret == 'ok') { ret = RemoteObj.Exec('start', ''); } } else err.description = 'An error occurred loading the environment template'; } else if (ret == 'alreadyset') { // The machine is busy. Reset it if necessary.
return false; } else err.description = 'An error occurred loading the build template'; } else err.description = 'An error occurred setting the timestamp.';
if (ret != 'ok') { if (!err.description) err.description = 'A failure occurred communicating with the machine.';
err.details = ret;
throw(err); }
return true; }
function LoadEnvironmentTemplate() { var xml = new ActiveXObject('Microsoft.XMLDOM'); var err = new Error(); var node;
fStandaloneMode = false;
xml.async = false; // It's unlikely they have the schema file available for this template,
// so we turn off schema validation right now. The script engine will
// validate it when we start the build.
xml.validateOnParse = false; xml.resolveExternals = false;
if (!xml.load(strEnviroURL) || !xml.documentElement) { err.description = 'Error loading the environment template ' + strEnviroURL; err.details = xml.parseError.reason;
throw(err); }
node = xml.documentElement.selectSingleNode('BuildManager');
if (!node) { err.description = 'Invalid environment template file (BuildManager tag missing): ' + strEnviroURL; throw(err); }
strBldMgr = node.getAttribute("Name"); strBldMgrIdentity = node.getAttribute("Identity");
if (!strBldMgr) { err.description = 'Invalid environment template file (BuildManager tag badly formatted): ' + strEnviroURL; throw(err); } if (!strBldMgrIdentity) strBldMgrIdentity = DEFAULT_IDENTITY_BM;
if (strBldMgr.toLowerCase() == '%localmachine%' || strBldMgr.toLowerCase() == '%remotemachine%') { err.description = 'Sorry, cannot use the local machine or remote machine templates from this script';
throw(err); }
strEnviroXML = 'XML: ' + xml.xml;
if (fCreateMachines) { var node; var strPostBuild; var nodelist; var objMach;
// Build the list of machines so we can copy the binaries from each
// one.
strPostBuild = node.getAttribute("PostBuildMachine");
nodelist = xml.documentElement.selectNodes('Machine');
for (node = nodelist.nextNode(); node; node = nodelist.nextNode()) { objMach = new MachineInfo();
objMach.strName = node.getAttribute("Name"); objMach.strEnlistment = node.getAttribute("Enlistment"); objMach.Identity = node.getAttribute("Identity"); if (!objMach.Identity || objMach.Identity == '') objMach.Identity = DEFAULT_IDENTITY_BUILDER;
if (objMach.strName.toLowerCase() == strPostBuild.toLowerCase()) { objMach.fPostBuild = true; }
aMachines[aMachines.length] = objMach; } }
return true; }
function LoadConfigTemplate() { var xml = new ActiveXObject('Microsoft.XMLDOM'); var err = new Error();
xml.async = false; // It's unlikely they have the schema file available for this template,
// so we turn off schema validation right now. The script engine will
// validate it when we start the build.
xml.validateOnParse = false; xml.resolveExternals = false;
if (!xml.load(strConfigURL) || !xml.documentElement) { err.description = 'Error loading the config template ' + strConfigURL; err.details = xml.parseError.reason;
throw(err); }
strConfigXML = 'XML: ' + xml.xml;
return true; }
function GetBinariesUNC(mach) { var strUNC;
strUNC = '\\\\' + mach.strName + '\\';
try { var objOD = new ActiveXObject('MTScript.ObjectDaemon', mach.strName) RemoteObj = objOD.OpenInterface(mach.Identity, 'MTScript.Remote', true); } catch(ex) { err.description = 'Sorry. Could not connect to machine "' + mach.strName + '"';
err.description += '\n\tVerify that the machine is available and that mtscript.exe\n\tis running on the machine.';
err.details = ex.description;
throw(err); }
try { strUNC += eval(RemoteObj.Exec('getpublic', 'PrivateData.aEnlistmentInfo[0].hEnvObj["_nttree"]')); } catch(ex) { err.description = 'Machine ' + mach.strName + '\\' + mach.Identity + ' is not in the correct state. '; err.description += 'The binaries location cannot be determined. (Was the machine reset?)';
err.details = ex.description;
throw(err); }
strUNC = strUNC.replace(/\:/ig, '$');
return strUNC; }
function DoBinariesUpdate() { try { LoadEnvironmentTemplate(); LoadConfigTemplate();
// The postbuild machine must always be the first machine listed
var i;
for (i = 0; i < aMachines.length; i++) { if (aMachines[i].fPostBuild) { WScript.Echo(aMachines[i].strName + "\\" + aMachines[i].Identity + " " + GetBinariesUNC(aMachines[i])); } }
for (i = 0; i < aMachines.length; i++) { if (!aMachines[i].fPostBuild) { WScript.Echo(aMachines[i].strName + "\\" + aMachines[i].Identity + " " + GetBinariesUNC(aMachines[i])); } } } catch(ex) { WScript.Echo('An error occurred during update binaries:'); WScript.Echo('\t' + ex); WScript.Echo(''); return 1; } return 0; }
function Error_ToString() { var i; var str = 'Exception(';
/* Only some error messages get filled in for "ex". Specifically the text for disk full never seems to get set by functions such as CreateTextFile().
*/ if (this.number != null && this.description == "") { switch(this.number) { case -2147024784: this.description = "There is not enough space on the disk."; break; case -2147024894: this.description = "The system cannot find the file specified."; break; case -2147023585: this.description = "There are currently no logon servers available to service the logon request."; break; case -2147023170: this.description = "The remote procedure call failed."; break; case -2147024837: this.description = "An unexpected network error occurred"; break; case -2147024890: this.description = "The handle is invalid."; break; default: this.description = "Error text not set for (" + this.number + ")"; break; } } for(i in this) { str += i + ": " + this[i] + " "; } return str + ")"; }
// MyEval(expr)
// evaluating uneval'ed objects creates a bunch of junk local variables.
// by putting the eval call in a little subroutine, we avoid keeping those
// locals around.
function MyEval(expr) { try { return eval(expr); } catch(ex) { throw ex; } } // CopyFileNoThrow(strSrc, strDst)
// Wrap the FSObj.CopyFile call to prevent it from
// throwing its errors.
function CopyFileNoThrow(strSrc, strDst) { try { g_FSObj.CopyFile(strSrc, strDst, true); } catch(ex) { WScript.Echo("Copy failed from " + strSrc + " to " + strDst + " " + ex); return ex; } return null; }
// CreateFolderNoThrow(strSrc, strDst)
// Wrap the FSObj.MakeFolder call to prevent it from
// throwing its errors.
function CreateFolderNoThrow(strName) { try { g_FSObj.CreateFolder(strName); } catch(ex) { return ex; } return null; }
// DirScanNoThrow(strDir)
// Wrap the FSObj.Directory scan functionality to prevent it from
// throwing its errors.
function DirScanNoThrow(strDir) { var aFiles = new Array(); try { var folder; var fc;
folder = g_FSObj.GetFolder(strDir); fc = new Enumerator(folder.files); for (; !fc.atEnd(); fc.moveNext()) { aFiles[aFiles.length] = fc.item().Name; // fc.item() returns entire path, fc.item().Name is just the filename
} } catch(ex) { aFiles.ex = ex; } return aFiles; }
//
// CopyDirectory
// Do a non-recursive directory copy.
//
function CopyDirectory(strSrcDir, strDestDir) { var ret = true; var ex; var aFiles; var strFileName = ''; var i;
WScript.Echo("Copy from " + strSrcDir + " to " + strDestDir); aFiles = DirScanNoThrow(strSrcDir); if (aFiles.ex) { WScript.Echo('Could not dirscan ' + strSrcDir + ', ex=' + ex); return false; } else { for(i = 0; i < aFiles.length; ++i) { strFileName = aFiles[i]; ex = CopyFileNoThrow( strSrcDir + '\\' + strFileName, strDestDir + '\\' + strFileName);
if (ex) { WScript.Echo("\t FAILED: " + strFileName); ret = false; } else WScript.Echo("\t COPIED: " + strFileName); } } return ret; }
//
// CopyMTScriptLog()
// Copy the highest numbers "mtscript" log file
// from the specified machine.
//
function CopyMTScriptLog(strMachineName, strIdentity, strDestDir) { var nLargestIndex = 0; var aReResult; var re = new RegExp('^' + strMachineName + '_' + strIdentity + '_MTS.([0-9]+).log$', 'i'); // match files "BUILDCON2_MTScript.051.log"
var ex; var aFiles; var strFileName = ''; var i; var strSrcDir = '\\\\' + strMachineName + "\\bc_build_logs";
aFiles = DirScanNoThrow(strSrcDir); if (aFiles.ex) WScript.Echo('Could not dirscan ' + strSrcDir + ', ex=' + ex ); else { for(i = 0; i < aFiles.length; ++i) { aReResult = re.exec(aFiles[i]); if (aReResult && Number(aReResult[1]) > nLargestIndex) { nLargestIndex = Number(aReResult[1]); strFileName = aFiles[i]; } } if (strFileName == '') WScript.Echo("No MTSCRIPT log files on " + strMachineName); else { WScript.Echo("Copy from " + strSrcDir + '\\' + strFileName + " to " + strDestDir + '\\' + g_FSObj.GetFilename(strFileName)); ex = CopyFileNoThrow(strSrcDir + '\\' + strFileName, strDestDir + '\\' + g_FSObj.GetFilename(strFileName)); if (ex) { WScript.Echo("Failed " + ex); return false; } } } return true; }
//
// CopyLogFiles()
// Copy the build log files from the given machines.
//
function CopyLogFiles(strDestDir, strBuildManagerName, strBuildManagerIdentity, aMachines) { var i; var strDest;
CreateFolderNoThrow(strDestDir); CopyMTScriptLog(strBuildManagerName, strBuildManagerIdentity, strDestDir); for( i = 0; i < aMachines.length; ++i) { CopyMTScriptLog(aMachines[i].strName, aMachines[i].Identity, strDestDir); } /* Note: The following code tries to guess the filenames of the build log files. The "real" information can be had from the PublicData of the build manager. PublicData.aBuild[0].aDepot[ x ].aTask[ y ].strLogPath PublicData.aBuild[0].aDepot[ x ].aTask[ y ].strErrLogPath
Of course, that would require that the build manager is still alive and well. */ for( i = 0; i < aMachines.length; ++i) { strDest = strDestDir + "\\" + aMachines[i].strName; CreateFolderNoThrow(strDest);
strDest += '\\' + aMachines[i].Identity; CreateFolderNoThrow(strDest);
CopyDirectory("\\\\" + aMachines[i].strName + "\\bc_build_logs\\build_logs\\" + aMachines[i].Identity, strDestDir + "\\" + aMachines[i].strName + '\\' + aMachines[i].Identity); } }
//
// CaptureLogsManager()
// Connect to the given build manager
// and query it for its list of build machines,
// The copy the MTSCRIPT and build log files from
// each of those machines.
//
function CaptureLogsManager(strLogDir, strMachineName, strIdentity) { var strMode; var obj; try { WScript.Echo("Attempting connection to " + strMachineName + "\\" + strIdentity); obj = new ActiveXObject('MTScript.Proxy'); WScript.Echo("Now connecting"); obj.ConnectToMTScript(strMachineName, strIdentity, false);
strMode = obj.Exec('getpublic', 'PublicData.strMode'); strMode = MyEval(strMode); if (strMode == 'idle') { WScript.Echo(strMachineName + "\\" + strIdentity + " is currently idle"); return 1; }
aMachines = obj.Exec('getpublic', 'PrivateData.objEnviron.Machine'); aMachines = MyEval(aMachines); for( i = 0; i < aMachines.length; ++i) { aMachines[i].strName = aMachines[i].Name; if (!aMachines[i].Identity || aMachines[i].Identity == '') aMachines[i].Identity = DEFAULT_IDENTITY_BUILDER; } CopyLogFiles(strLogDir, strMachineName, strIdentity, aMachines); } catch(ex) { WScript.Echo("CaptureLogsManager '" + strMachineName + "\\" + strIdentity + "' failed, ex=" + ex); return 1; } return 0; }
//
// CaptureLogsEnviro()
// Read the supplied environment template
// and copy the MTSCRIPT and build logs
// from each of the specifed build machines.
//
function CaptureLogsEnviro(strLogDir) { try { var ret; if (!strEnviroURL && strConfigURL) { strEnviroURL = strConfigURL; strConfigURL = null; } LoadEnvironmentTemplate();
ret = CopyLogFiles(strLogDir, strBldMgr, strBldMgrIdentity, aMachines); } catch(ex) { WScript.Echo('An error occurred capturing log files:'); WScript.Echo('\t ex=' + ex); WScript.Echo(''); return 1; } return 0; }
/* CommonVersionCheck(strLocalVersion, PublicData)
Ensure that this script and the remote script are running the same version. A copy of this function exists in: autostart.js bldcon.hta utils.js */ function CommonVersionCheck(strLocalVersion, PublicData) { var err = new Error(); var reBuildNum = new RegExp("V\\(([#0-9. ]+)\\)"); var aLocal = reBuildNum.exec(strLocalVersion); var aPublicBuildNum;
if (!PublicData.strDataVersion) { err.description = "autostart version mismatch"; err.details = "Build Manager does not have a version string"; throw err; } aPublicBuildNum = reBuildNum.exec(PublicData.strDataVersion); if (!aLocal || !aPublicBuildNum) { err.description = "Invalid version format"; err.details = "UI Version: " + strLocalVersion + ", Build Manager Version: " + PublicData.strDataVersion; throw err; }
if (aLocal[1] != aPublicBuildNum[1]) { err.description = "Version mismatch"; err.details = "UI Version: " + strLocalVersion + ", Build Manager Version: " + PublicData.strDataVersion; throw err; } }
function Usage(x) { WScript.Echo(''); WScript.Echo('Usage: bcauto [-f] [-u] [-t] <config> <enviro>'); WScript.Echo(''); WScript.Echo(' -f : If the machine is busy, terminate the build and restart.'); WScript.Echo(' -u : Update: Recombine all the binaries from each of the build'); WScript.Echo(' machines after taking incremental fixes so that postbuild'); WScript.Echo(' can be run again.'); WScript.Echo(' -t <timestamp> : Use timestamp (e.g. 2001/12/15:12:00)'); WScript.Echo(' -log dir : Capture log files to specified directory'); WScript.Echo(' -logman machine identity : Query "machine" with "identity" for list of'); WScript.Echo(' build machines instead of template files.'); WScript.Echo(' ("machine" must be a build manager)'); WScript.Echo(' config : URL or path for the build template.'); WScript.Echo(' enviro : URL or path for the environment template.'); WScript.Echo(''); WScript.Quit(1); }
|