//+--------------------------------------------------------------------------- // // 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] '); 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 : 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); }