Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

819 lines
26 KiB

  1. Include('types.js');
  2. Include('utils.js');
  3. var g_aStringMap = null;
  4. var g_FSObj;
  5. var g_fStandAlone = false;
  6. var ALREADYSET = "alreadyset";
  7. var g_fRestarting = false;
  8. var g_strVersionError = "";
  9. var g_fVersionOK = true;
  10. var g_strVersion = /* $DROPVERSION: */ "V(########) F(!!!!!!!!!!!!!!)" /* $ */;
  11. function mtscript_js::OnScriptError(strFile, nLine, nChar, strText, sCode, strSource, strDescription)
  12. {
  13. return CommonOnScriptError("mtscript_js", strFile, nLine, nChar, strText, sCode, strSource, strDescription);
  14. }
  15. //+---------------------------------------------------------------------------
  16. //
  17. // Member: mtscript_js::ScriptMain, public
  18. //
  19. // Synopsis: Main entrypoint for the script. The ScriptMain event is
  20. // fired after the script has been loaded and initialized.
  21. //
  22. // This file (mtscript.js) is the primary script for the
  23. // multi-threaded scripting engine. As a result, it will never
  24. // be unloaded and it is safe to exit from ScriptMain. It will
  25. // then just sit around handling events.
  26. //
  27. // Note: No other events will be fired in this script until
  28. // ScriptMain() returns. This is not true for other scripts
  29. // launched using SpawnScript(), since for them their
  30. // thread terminates when ScriptMain exits.
  31. //
  32. //----------------------------------------------------------------------------
  33. function mtscript_js::ScriptMain()
  34. {
  35. Initialize();
  36. CheckHostVersion(g_strVersion);
  37. LogMsg("Script version: " + g_strVersion);
  38. }
  39. function CheckHostVersion(strVersion)
  40. {
  41. var nMajor = 0;
  42. var nMinor = 0;
  43. var aParts;
  44. var strError = "";
  45. var a = g_reBuildNum.exec(strVersion);
  46. if (a)
  47. {
  48. aParts = a[1].split('.');
  49. nMajor = aParts[0];
  50. if (aParts.length > 1)
  51. nMinor = aParts[1];
  52. if (nMajor != HostMajorVer || nMinor != HostMinorVer)
  53. {
  54. if (HostMajorVer) // Only complain if mtscript.exe is a released version.
  55. {
  56. strError = "(" + HostMajorVer + "." + HostMinorVer + ") != (" + nMajor + "." + nMinor + ")";
  57. SimpleErrorDialog("MTScript.exe version does not match scripts.", strError, false);
  58. g_strVersionError = "MTScript.exe version does not match scripts: " + strError;
  59. g_fVersionOK = false;
  60. }
  61. }
  62. }
  63. if (g_fVersionOK)
  64. {
  65. g_fVersionOK = CommonVersionCheck(strVersion);
  66. if (!g_fVersionOK)
  67. g_strVersionError = "Scripts version mismatch: " + strVersion;
  68. }
  69. }
  70. function mtscript_js::OnEventSourceEvent(RemoteObj, DispID, cmd, params)
  71. {
  72. LogMsg("MTSCRIPT received " + cmd);
  73. }
  74. function mtscript_js::OnProcessEvent(pid, evt, param)
  75. {
  76. ASSERT(false, 'OnProcessEvent('+pid+', '+evt+', '+param+') received!');
  77. }
  78. //+---------------------------------------------------------------------------
  79. //
  80. // Member: mtscript_js::OnRemoteExec, public
  81. //
  82. // Synopsis: Event handler which is called when a remote machine calls
  83. // IConnectedMachine::Exec.
  84. //
  85. // Arguments: [cmd] -- Parameters given by the caller.
  86. // [params] --
  87. //
  88. // Returns: 'ok' on success, error string on failure.
  89. //
  90. //----------------------------------------------------------------------------
  91. function mtscript_js::OnRemoteExec(cmd, params)
  92. {
  93. if (g_fRestarting)
  94. {
  95. LogMsg("rejecting OnRemoteExec command " + cmd);
  96. if (cmd == "getpublic")
  97. return "throw new Error(-1, 'getpublic \\'" + params + "\\' failed');";
  98. return 'restarting';
  99. }
  100. var vRet = 'ok';
  101. if (cmd != 'getpublic' && cmd != 'getdepotupdate')
  102. {
  103. LogMsg('Received ' + cmd + ' command from remote machine.');
  104. }
  105. switch (cmd)
  106. {
  107. case 'setstringmap':
  108. if (g_fVersionOK)
  109. vRet = StoreStringMap(params);
  110. else
  111. vRet = "version mismatch(setstringmap): " + g_strVersionError;
  112. break;
  113. case 'setmode':
  114. vRet = ChangeMode(params.toLowerCase());
  115. break;
  116. case 'setconfig':
  117. vRet = SetConfigTemplate(params);
  118. break;
  119. case 'setenv':
  120. vRet = SetEnvironTemplate(params);
  121. LogMsg("Environ is :" + PrivateData.objEnviron);
  122. break;
  123. case 'getpublic':
  124. vRet = GetPublic(params);
  125. break;
  126. case 'getdepotupdate':
  127. vRet = GetDepotUpdate(params);
  128. break;
  129. case 'debug':
  130. JAssert(false);
  131. break;
  132. case 'dialog':
  133. ManageDialog(params);
  134. break;
  135. case 'exitprocess':
  136. ExitProcess();
  137. break;
  138. // DEBUG ONLY - allow user to execute arbitrary commands.
  139. case 'eval':
  140. LogMsg("Evaluating '" + params + "'");
  141. vRet = MyEval(params);
  142. LogMsg("Eval result is '" + vRet + "'");
  143. break;
  144. default:
  145. if (PrivateData.fnExecScript != null)
  146. {
  147. vRet = PrivateData.fnExecScript(cmd, params);
  148. }
  149. else
  150. {
  151. vRet = 'invalid command: ' + cmd;
  152. }
  153. break;
  154. }
  155. return vRet;
  156. }
  157. //+---------------------------------------------------------------------------
  158. //
  159. // Member: mtscript_js::OnMachineConnect, public
  160. //
  161. // Synopsis: Called when a remote machine connects.
  162. //
  163. //----------------------------------------------------------------------------
  164. function mtscript_js::OnMachineConnect()
  165. {
  166. LogMsg('A machine connected!');
  167. }
  168. //+---------------------------------------------------------------------------
  169. //
  170. // Function: Initialize
  171. //
  172. // Synopsis: Initialize main data structures
  173. //
  174. //----------------------------------------------------------------------------
  175. function Initialize()
  176. {
  177. PublicData = new PublicDataObj();
  178. PrivateData = new PrivateDataObj();
  179. g_FSObj = new ActiveXObject('Scripting.FileSystemObject');
  180. PrivateData.fileLog = null;
  181. // NOTE: Its important that utilthrd.js gets started
  182. // early set it setup a bunch of global utility functions
  183. // in PrivateData.
  184. // Specifically, "LogMsg" depends on this!
  185. SpawnScript('utilthrd.js');
  186. WaitForSync('UtilityThreadReady', 0);
  187. PrivateData.fEnableLogging = true;
  188. LogMsg("ScriptMain()\n");
  189. PublicData.aBuild[0] = new Build();
  190. PrivateData.objConfig = null;
  191. PrivateData.objEnviron = null;
  192. }
  193. //+---------------------------------------------------------------------------
  194. //
  195. // Function: ChangeMode
  196. //
  197. // Synopsis: Called when we need to set the mode of this machine.
  198. //
  199. // Arguments: [strMode] -- Mode to switch to.
  200. //
  201. // Notes: It is only valid to go from 'idle' to another mode, or from
  202. // some other mode to 'idle'. You cannot switch directly between
  203. // other modes.
  204. //
  205. //----------------------------------------------------------------------------
  206. function ChangeMode(strMode)
  207. {
  208. var strWaitSyncs;
  209. var vRet = 'ok';
  210. var strRealMode = strMode;
  211. if (strMode == PublicData.strMode)
  212. {
  213. return 'ok';
  214. }
  215. if (!g_fVersionOK && strMode != 'idle')
  216. return 'version mismatch(ChangeMode): ' + g_strVersionError;
  217. switch (strMode)
  218. {
  219. case 'idle':
  220. LogMsg('Shutting down script threads...' + strRealMode);
  221. if (PublicData.strMode != 'idle')
  222. {
  223. if (PrivateData.fnExecScript != null)
  224. {
  225. vRet = PrivateData.fnExecScript('terminate', 0);
  226. }
  227. SignalThreadSync(strRealMode + "ThreadExit");
  228. }
  229. Sleep(1000);
  230. g_fRestarting = true;
  231. LogMsg('Restarting...');
  232. if (PrivateData.fileLog != null)
  233. {
  234. PrivateData.fEnableLogging = false;
  235. TakeThreadLock("PrivateData.fileLog");
  236. PrivateData.fileLog[0].Close();
  237. PrivateData.fileLog = null;
  238. ReleaseThreadLock("PrivateData.fileLog");
  239. }
  240. Restart();
  241. break;
  242. case 'slave':
  243. case 'master':
  244. case 'test':
  245. case 'stress':
  246. if (PublicData.strMode != 'idle')
  247. {
  248. vRet = 'can only switch to idle when in another mode';
  249. }
  250. else if ( strMode != 'test' && strMode != 'stress'
  251. && ( PublicData.aBuild[0].strConfigTemplate.length == 0
  252. || PublicData.aBuild[0].strEnvTemplate.length == 0))
  253. {
  254. vRet = 'both templates should be set before setting mode';
  255. }
  256. else
  257. {
  258. if (strMode == 'master' && g_fStandAlone)
  259. {
  260. PrivateData.fIsStandalone = true;
  261. strRealMode = 'slave';
  262. }
  263. LogMsg('Switching to ' + strMode + ' mode (realmode = ' + strRealMode + ').');
  264. strWaitSyncs = strRealMode + 'ThreadReady,' + strRealMode + 'ThreadFailed';
  265. ResetSync(strWaitSyncs);
  266. SpawnScript(strRealMode + '.js');
  267. var nWait = WaitForMultipleSyncs(strWaitSyncs, false, 0)
  268. if (nWait != 1)
  269. {
  270. vRet = 'mtscript::failed to launch mode script: ' + (nWait == 0 ? "timeout" : ("script " + strRealMode + ".js failed") );
  271. }
  272. else
  273. {
  274. PublicData.strMode = strMode;
  275. }
  276. }
  277. break;
  278. default:
  279. vRet = 'unknown mode: ' + strMode;
  280. break;
  281. }
  282. return vRet;
  283. }
  284. //+---------------------------------------------------------------------------
  285. //
  286. // Function: StoreStringMap
  287. //
  288. // Synopsis: The string map is used to substitute strings such as
  289. // %LocalMachine% in fields in the XML files with their true
  290. // values. This map is given to us by the UI.
  291. //
  292. // Arguments: [strMap] -- Map of "string=value" pairs (comma separated)
  293. //
  294. // Notes: The mapping "%LocalMachine%=<name of local machine>" is the
  295. // only default mapping.
  296. //
  297. //----------------------------------------------------------------------------
  298. function StoreStringMap(strMap)
  299. {
  300. var re = /[,;]/ig
  301. var d = new Date();
  302. var ms;
  303. var d2;
  304. if (strMap != null && strMap.length == 0)
  305. {
  306. return 'string map cannot be empty';
  307. }
  308. // strMap will be null if called by SetConfigTemplate/SetEnvironTemplate.
  309. // If the map has already been configured, just return.
  310. if (g_aStringMap && !strMap)
  311. {
  312. return;
  313. }
  314. if (!strMap)
  315. {
  316. g_aStringMap = new Array();
  317. strMap = '';
  318. }
  319. else
  320. {
  321. g_aStringMap = strMap.split(re);
  322. }
  323. // Put in our predefined values if the caller is not defining them for us.
  324. // %localmachine% should always expand to our name, so if the caller
  325. // specifies it then we override it. This will happen if we are a slave.
  326. if (strMap.toLowerCase().indexOf('%localmachine%=') == -1)
  327. {
  328. g_aStringMap[g_aStringMap.length] = '%LocalMachine%=' + LocalMachine;
  329. }
  330. else
  331. {
  332. var i;
  333. for (i = 0; i < g_aStringMap.length; i++)
  334. {
  335. if (g_aStringMap[i].toLowerCase().indexOf('%localmachine%') != -1)
  336. {
  337. g_aStringMap[i] = '%LocalMachine%=' + LocalMachine;
  338. break;
  339. }
  340. }
  341. }
  342. if (strMap.toLowerCase().indexOf('%today%=') == -1)
  343. {
  344. g_aStringMap[g_aStringMap.length] = '%Today%=' + d.DateToSDString(true);
  345. }
  346. ms = d.getTime() - 86400000; // Subtract one day in milliseconds
  347. d2 = new Date(ms);
  348. if (strMap.toLowerCase().indexOf('%yesterday%=') == -1)
  349. {
  350. g_aStringMap[g_aStringMap.length] = '%Yesterday%=' + d2.DateToSDString(true);
  351. }
  352. // Store the string map so we can give it to the slave machines.
  353. PrivateData.aStringMap = g_aStringMap;
  354. return 'ok';
  355. }
  356. //+---------------------------------------------------------------------------
  357. //
  358. // Function: SetConfigTemplate
  359. //
  360. // Synopsis: Loads the given configuration template (XML file)
  361. //
  362. // Arguments: [strURL] -- URL to load from (must be a URL)
  363. //
  364. //----------------------------------------------------------------------------
  365. function SetConfigTemplate(strURL)
  366. {
  367. var vRet = 'ok';
  368. var strExpandedURL;
  369. // Ensure our string map has been set
  370. StoreStringMap(null);
  371. if (PublicData.aBuild[0].strConfigTemplate.length > 0)
  372. {
  373. // bugbug check to see that we are not changing the templates.
  374. LogMsg( 'config template is already set');
  375. vRet = ALREADYSET;
  376. }
  377. else
  378. {
  379. LogMsg('Loading configuration template...');
  380. var re = /%ScriptPath%/gi;
  381. strExpandedURL = strURL.replace(re, ScriptPath);
  382. PrivateData.objConfig = new Object();
  383. vRet = PrivateData.objUtil.fnLoadXML(PrivateData.objConfig,
  384. strExpandedURL,
  385. 'config_schema.xml',
  386. g_aStringMap);
  387. if (vRet == 'ok')
  388. vRet = ValidateConfigTemplate(PrivateData.objConfig);
  389. if (vRet == 'ok')
  390. {
  391. PublicData.aBuild[0].strConfigTemplate = strURL.replace(/\r\n/g, "");
  392. PublicData.aBuild[0].objBuildType.strConfigLongName = PrivateData.objConfig.LongName;
  393. PublicData.aBuild[0].objBuildType.strConfigDescription = PrivateData.objConfig.Description;
  394. }
  395. if (vRet != 'ok')
  396. {
  397. PrivateData.objConfig = null;
  398. PrivateData.objEnviron = null;
  399. PublicData.aBuild[0].strConfigTemplate = '';
  400. PublicData.aBuild[0].strEnvTemplate = '';
  401. if (strURL.slice(0, 5) != 'XML: ')
  402. vRet = "Error setting configuration template: " + strURL + "'\n" + vRet;
  403. }
  404. }
  405. return vRet;
  406. }
  407. //+---------------------------------------------------------------------------
  408. //
  409. // Function: SetEnvironTemplate
  410. //
  411. // Synopsis: Loads the given environment template (XML file)
  412. //
  413. // Arguments: [strURL] -- URL to load from (must be a URL)
  414. //
  415. //----------------------------------------------------------------------------
  416. function SetEnvironTemplate(strURL)
  417. {
  418. var vRet = 'ok';
  419. var strExpandedURL;
  420. // Ensure our string map has been set
  421. StoreStringMap(null);
  422. if (PublicData.aBuild[0].strEnvTemplate.length > 0)
  423. {
  424. // bugbug check to see that we are not changing the templates.
  425. LogMsg('environment template is already set');
  426. }
  427. else
  428. {
  429. LogMsg('Loading environment template...');
  430. var re = /%ScriptPath%/gi;
  431. strExpandedURL = strURL.replace(re, ScriptPath);
  432. PrivateData.objEnviron = new Object();
  433. vRet = PrivateData.objUtil.fnLoadXML(PrivateData.objEnviron,
  434. strExpandedURL,
  435. 'enviro_schema.xml',
  436. g_aStringMap);
  437. if (vRet == 'ok')
  438. vRet = ValidateEnvironTemplate(PrivateData.objEnviron);
  439. if (vRet == 'ok')
  440. {
  441. PublicData.aBuild[0].strEnvTemplate = strURL.replace(/\r\n/g, "");
  442. PublicData.aBuild[0].objBuildType.strEnviroLongName = PrivateData.objEnviron.LongName;
  443. PublicData.aBuild[0].objBuildType.strEnviroDescription = PrivateData.objEnviron.Description;
  444. PublicData.aBuild[0].objBuildType.strPostBuildMachine = PrivateData.objEnviron.BuildManager.PostBuildMachine;
  445. PublicData.aBuild[0].objBuildType.fDistributed = !g_fStandAlone;
  446. }
  447. if (vRet != 'ok')
  448. {
  449. PrivateData.objConfig = null;
  450. PrivateData.objEnviron = null;
  451. PublicData.aBuild[0].strConfigTemplate = '';
  452. PublicData.aBuild[0].strEnvTemplate = '';
  453. if (strURL.slice(0, 5) != 'XML: ')
  454. vRet = "Error setting environment template: " + strURL + "'\n" + vRet;
  455. }
  456. LogMsg('Standalone mode = ' + g_fStandAlone);
  457. }
  458. return vRet;
  459. }
  460. /*
  461. GetDepotUpdate();
  462. Optimized status update.
  463. This function allows the UI to retrieve only the
  464. status for the depots which have changed from the
  465. last time the UI retrieved status.
  466. This significantly reduces the load on the build manager
  467. machine and cuts down a bit on network traffic.
  468. The UI passes in an object initializer to be evaluated.
  469. The objects properties are of the form:
  470. "MachineName,DepotName"
  471. The value for each property is the most objUpdateCount.nCount value
  472. for the given depot that the UI has seen.
  473. This function returns status for depots which have a greater
  474. objUpdateCount.nCount value, and all depots which are not specified by
  475. the UI (Presumably the UI will not list the depots that it
  476. has not yet seen any status on).
  477. */
  478. function GetDepotUpdate(params)
  479. {
  480. try
  481. {
  482. var strDepotName;
  483. var objDepot = MyEval(params); // { "JPORKKA,COM":45, "jporkka,termserv":17}
  484. var aDepot = new Array();
  485. var strStatName;
  486. var fAll = false;
  487. var fGotRoot;
  488. if (objDepot == null)
  489. fAll = true;
  490. for(i = 0; i < PublicData.aBuild[0].aDepot.length; ++i)
  491. {
  492. if (PublicData.aBuild[0].aDepot[i] != null)
  493. {
  494. strDepotName = PublicData.aBuild[0].aDepot[i].strName;
  495. strStatName = PublicData.aBuild[0].aDepot[i].strMachine + ',' + strDepotName;
  496. if (fAll ||
  497. objDepot[strStatName] == null ||
  498. PublicData.aBuild[0].aDepot[i].fDisconnected ||
  499. objDepot[strStatName] < PublicData.aBuild[0].aDepot[i].objUpdateCount.nCount)
  500. {
  501. aDepot[i] = PublicData.aBuild[0].aDepot[i];
  502. }
  503. }
  504. }
  505. return PrivateData.objUtil.fnUneval(aDepot);
  506. }
  507. catch(ex)
  508. {
  509. LogMsg("failed (" + params + ")" + ex);
  510. return "throw new Error(-1, 'GetDepotUpdate \\'" + params + "\\' failed');";
  511. }
  512. }
  513. var filePublicDataLog;
  514. function GetPublic(params)
  515. {
  516. var vRet = "";
  517. var strLogData;
  518. var f_DidUneval = false;
  519. try
  520. {
  521. switch (params)
  522. {
  523. case 'root':
  524. vRet = PrivateData.objUtil.fnUneval(PublicData);
  525. f_DidUneval = true;
  526. // no need to test if Options.LogDir exists -- handled by the catch() code below.
  527. if (filePublicDataLog)
  528. filePublicDataLog[0] = g_FSObj.CreateTextFile(filePublicDataLog[1], true);
  529. else
  530. filePublicDataLog = PrivateData.objUtil.fnCreateNumberedFile("PublicData.log", MAX_MSGS_FILES);
  531. filePublicDataLog[0].Write(vRet);
  532. filePublicDataLog[0].Close();
  533. break;
  534. default:
  535. var obj = eval(params);
  536. vRet = PrivateData.objUtil.fnUneval(obj);
  537. f_DidUneval = true;
  538. }
  539. }
  540. catch(ex)
  541. {
  542. LogMsg("failed (" + params + ") " + ex);
  543. try
  544. {
  545. if (filePublicDataLog[0])
  546. filePublicDataLog[0].Close();
  547. }
  548. catch(ex)
  549. {
  550. LogMsg("Close publicdatalog failed " + ex);
  551. }
  552. if (!f_DidUneval)
  553. {
  554. // We don't want to throw if there was a logging error, but
  555. // we do want to throw if the exception was before Uneval
  556. // NOTE: Since throwing across machine boundaries is bad
  557. // we simply return a throw command as a string.
  558. // If this string is eval()ed, then it will throw.
  559. // The caller must be prepared to handle this.
  560. return "throw new Error(-1, 'getpublic \\'" + params + "\\' failed');";
  561. }
  562. }
  563. return vRet;
  564. }
  565. // ManageDialog(params)
  566. // This function is invoked by the UI to chage the status of PublicData.objDialog.
  567. // params is expected to be a string with comma seperated fields.
  568. // The first field is the action
  569. // The second field is the dialog index
  570. // Any remaining fields are dependant on the action.
  571. function ManageDialog(params)
  572. {
  573. LogMsg("*** ManageDialog");
  574. TakeThreadLock('Dialog');
  575. try
  576. {
  577. var aParams = params.split(',');
  578. JAssert(aParams.length != 2 || aParams[1] <= PublicData.objDialog.cDialogIndex, "dialog index bug: params[1]=" + aParams[1] + " > PD.objDialog.cDialogIndex(" + PublicData.objDialog.cDialogIndex + ")");
  579. if (aParams.length > 1 && aParams[1] == PublicData.objDialog.cDialogIndex)
  580. {
  581. if (aParams[0] == 'hide')
  582. PublicData.objDialog.fShowDialog = false;
  583. // FUTURE: in the dismiss case aParams[2] has the return
  584. // code for the dialog.
  585. // if (aParams[0] == 'dismiss')
  586. // PublicData.objDialog.fShowDialog = false;
  587. }
  588. }
  589. catch(ex)
  590. {
  591. LogMsg("" + ex);
  592. }
  593. ReleaseThreadLock('Dialog');
  594. }
  595. function ValidateConfigTemplate(obj)
  596. {
  597. var nDepotIdx;
  598. var hDepotNames;
  599. EnsureArray(obj, "Depot");
  600. hDepotNames = new Object;
  601. // Ensure that depot names are not duplicated
  602. for(nDepotIdx = 0; nDepotIdx < obj.Depot.length; ++nDepotIdx)
  603. {
  604. if (hDepotNames[obj.Depot[nDepotIdx].Name.toUpperCase()] != null)
  605. return "Duplicate depot name '" + obj.Depot[nDepotIdx].Name + "'";
  606. hDepotNames[obj.Depot[nDepotIdx].Name.toUpperCase()] = nDepotIdx;
  607. }
  608. // Make sure the root depot is listed.
  609. if (typeof(hDepotNames['ROOT']) != 'number')
  610. {
  611. return 'Missing Root depot in config file';
  612. }
  613. return 'ok';
  614. }
  615. function ValidateEnvironTemplate(obj)
  616. {
  617. var nMachineIdx;
  618. var nDepotIdx;
  619. var hDepotNames;
  620. var hMachineNames;
  621. var objDepot;
  622. var cBuildMachines = 0;
  623. hDepotNames = new Object;
  624. hMachineNames = new Object;
  625. EnsureArray(obj, "Machine");
  626. // Ensure machine names are not duplicated
  627. // Ensure that depot names are not duplicated in the same machine
  628. // or accross machines.
  629. for(nMachineIdx = 0; nMachineIdx < obj.Machine.length; ++nMachineIdx)
  630. {
  631. obj.Machine[nMachineIdx].Name = obj.Machine[nMachineIdx].Name.toUpperCase();
  632. if (hMachineNames[obj.Machine[nMachineIdx].Name] != null)
  633. return "Duplicate machine name " + obj.Machine[nMachineIdx].Name;
  634. if (obj.Machine[nMachineIdx].Enlistment == null)
  635. return "Missing Enlistment attribute on machine " + obj.Machine[nMachineIdx].Name;
  636. hMachineNames[obj.Machine[nMachineIdx].Name] = cBuildMachines;
  637. ++cBuildMachines;
  638. EnsureArray(obj.Machine[nMachineIdx], "Depot");
  639. objDepot = obj.Machine[nMachineIdx].Depot;
  640. for(nDepotIdx = 0; nDepotIdx < objDepot.length; ++nDepotIdx)
  641. {
  642. if (hDepotNames[objDepot[nDepotIdx].toUpperCase()] != null)
  643. {
  644. if (hDepotNames[objDepot[nDepotIdx].toUpperCase()] == obj.Machine[nMachineIdx].Name)
  645. return "Duplicate depot name '" + objDepot[nDepotIdx] + "'";
  646. else
  647. return "Duplicate depot name '" +
  648. objDepot[nDepotIdx] +
  649. "' specified on the machines: '" +
  650. hDepotNames[objDepot[nDepotIdx].toUpperCase()] +
  651. "' and '" +
  652. obj.Machine[nMachineIdx].Name +
  653. "'";
  654. }
  655. hDepotNames[objDepot[nDepotIdx].toUpperCase()] = obj.Machine[nMachineIdx].Name;
  656. }
  657. hDepotNames['ROOT'] = null; // OK for each machine to have its own root depot
  658. }
  659. // Determine standalone mode
  660. obj.BuildManager.Name = obj.BuildManager.Name.toUpperCase();
  661. g_fStandAlone = false;
  662. // Determine if this ia a standalone build
  663. // or a distributed build.
  664. // Ensure that the BuildManager machine is
  665. // not also a Build Machine in a distributed build.
  666. // Ensure that BuildManager specifies an enlistment
  667. // for a distributed build (and does not for a standalone build).
  668. if (hMachineNames[obj.BuildManager.Name] != null)
  669. {
  670. // If one of the build machines is also the build
  671. // manager, then there must only be one build machine
  672. if (cBuildMachines > 1)
  673. return "The machine " + obj.BuildManager.Name + " is listed as build manager and as a build machine in a distributed build";
  674. if (obj.BuildManager.Enlistment != null)
  675. return "BuildManager must not have an Enlistment attribute for a single machine build";
  676. if (obj.BuildManager.PostBuildMachine != null)
  677. return "BuildManager must not have an PostBuildMachine attribute for a single machine build";
  678. g_fStandAlone = true;
  679. }
  680. else
  681. {
  682. if (obj.BuildManager.PostBuildMachine == null)
  683. return "You must specify a postbuild machine in BuildManager";
  684. obj.BuildManager.PostBuildMachine = obj.BuildManager.PostBuildMachine.toUpperCase();
  685. if (hMachineNames[obj.BuildManager.PostBuildMachine] == null)
  686. return "The PostBuildMachine must be one of the build machines";
  687. nMachineIdx = hMachineNames[obj.BuildManager.PostBuildMachine];
  688. obj.BuildManager.Enlistment = obj.Machine[nMachineIdx].Enlistment;
  689. }
  690. if (cBuildMachines < 1)
  691. return "No build machines specified";
  692. return 'ok';
  693. }