Source code of Windows XP (NT5)
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.

467 lines
12 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1992 - 1995
  5. //
  6. // File: buildscr.cpp
  7. //
  8. // Contents: Implementation of the code which talks to the MTScript engine
  9. // when doing a distributed build using the build console.
  10. //
  11. //----------------------------------------------------------------------------
  12. #include "scrproc.h"
  13. #include "build.h"
  14. #include "buildscr.h"
  15. #define INITGUID
  16. #include <guiddef.h>
  17. DEFINE_GUID(CLSID_LocalScriptedProcess, 0x854c316f,0xc854,0x4a77,0xb1,0x89,0x60,0x68,0x59,0xe4,0x39,0x1b);
  18. DEFINE_GUID(IID_IScriptedProcess, 0x854c3171,0xc854,0x4a77,0xb1,0x89,0x60,0x68,0x59,0xe4,0x39,0x1b);
  19. DEFINE_GUID(IID_IScriptedProcessSink, 0x854c3172,0xc854,0x4a77,0xb1,0x89,0x60,0x68,0x59,0xe4,0x39,0x1b);
  20. DEFINE_GUID(CLSID_ObjectDaemon,0x854c3184,0xc854,0x4a77,0xb1,0x89,0x60,0x68,0x59,0xE4,0x39,0x1b);
  21. DEFINE_GUID(IID_IConnectedMachine,0x854c316c,0xc854,0x4a77,0xb1,0x89,0x60,0x68,0x59,0xe4,0x39,0x1b);
  22. DEFINE_GUID(IID_IObjectDaemon,0x854c3183,0xc854,0x4a77,0xb1,0x89,0x60,0x68,0x59,0xE4,0x39,0x1b);
  23. #define MAX_RETRIES 2
  24. HANDLE g_hMTEvent = NULL;
  25. HANDLE g_hMTThread = NULL;
  26. DWORD g_dwMTThreadId = 0;
  27. //+---------------------------------------------------------------------------
  28. //
  29. // Function: WaitForResume
  30. //
  31. // Synopsis: Sends a "phase complete" message to the script engine and then
  32. // waits for it to tell us to resume (if specified).
  33. //
  34. // Arguments: [fPause] -- If TRUE, we wait for a resume command
  35. // [pe] -- Message to send to the script engine
  36. //
  37. //----------------------------------------------------------------------------
  38. void
  39. WaitForResume(BOOL fPause, PROC_EVENTS pe)
  40. {
  41. if (g_hMTEvent)
  42. {
  43. HANDLE aHandles[2] = { g_hMTEvent, g_hMTThread };
  44. ResetEvent(g_hMTEvent);
  45. PostThreadMessage(g_dwMTThreadId, pe, 0, 0);
  46. if (fPause)
  47. {
  48. // Wait until either the event object is signaled or the thread dies
  49. WaitForMultipleObjects(2, aHandles, FALSE, INFINITE);
  50. }
  51. }
  52. }
  53. //+---------------------------------------------------------------------------
  54. //
  55. // Function: ExitMTScriptThread
  56. //
  57. // Synopsis: Tells the thread talking to the MTScript engine to exit.
  58. //
  59. //----------------------------------------------------------------------------
  60. void
  61. ExitMTScriptThread()
  62. {
  63. if (g_hMTEvent)
  64. {
  65. PostThreadMessage(g_dwMTThreadId, PE_EXIT, 0, 0);
  66. WaitForSingleObject(g_hMTThread, INFINITE);
  67. CloseHandle(g_hMTThread);
  68. CloseHandle(g_hMTEvent);
  69. }
  70. }
  71. //+---------------------------------------------------------------------------
  72. //
  73. // Function: SendStatus
  74. //
  75. // Synopsis: Sends a status message to the MTScript engine with the
  76. // current number of errors, warnings, and completed files.
  77. //
  78. // Arguments: [pSP] -- Pointer to MTScript engine interface
  79. //
  80. //----------------------------------------------------------------------------
  81. void
  82. SendStatus(IScriptedProcess *pSP)
  83. {
  84. wchar_t achBuf[300];
  85. long lRet;
  86. static ULONG cErrorsPrev = MAXDWORD;
  87. static ULONG cWarnPrev = MAXDWORD;
  88. static ULONG cFilesPrev = MAXDWORD;
  89. ULONG cErrors = NumberCompileErrors + NumberLibraryErrors + NumberLinkErrors + NumberBinplaceErrors;
  90. ULONG cWarn = NumberCompileWarnings + NumberLibraryWarnings + NumberLinkWarnings + NumberBinplaceWarnings;
  91. ULONG cFiles = NumberCompiles + NumberLibraries + NumberLinks /* + NumberBinplaces */;
  92. // Only send status if it's changed since last time we did it.
  93. if ( cErrors != cErrorsPrev
  94. || cWarn != cWarnPrev
  95. || cFiles != cFilesPrev)
  96. {
  97. cErrorsPrev = cErrors;
  98. cWarnPrev = cWarn;
  99. cFilesPrev = cFiles;
  100. wsprintfW(achBuf, L"errors=%d,warnings=%d,files=%d", cErrors, cWarn, cFiles);
  101. pSP->SendData(L"status", achBuf, &lRet);
  102. }
  103. }
  104. //+---------------------------------------------------------------------------
  105. //
  106. // Function: HandleMessage
  107. //
  108. // Synopsis: Handles a message that has come across our message queue.
  109. //
  110. // Arguments: [pmsg] -- Message
  111. // [pSP] -- Pointer to MTScript engine interface
  112. //
  113. //----------------------------------------------------------------------------
  114. BOOL
  115. HandleMessage(MSG *pmsg, IScriptedProcess *pSP)
  116. {
  117. long lRet;
  118. HRESULT hr = S_OK;
  119. switch (pmsg->message)
  120. {
  121. case PE_PASS0_COMPLETE:
  122. SendStatus(pSP);
  123. hr = pSP->SendData(L"pass 0 complete", L"", &lRet);
  124. break;
  125. case PE_PASS1_COMPLETE:
  126. SendStatus(pSP);
  127. hr = pSP->SendData(L"pass 1 complete", L"", &lRet);
  128. break;
  129. case PE_PASS2_COMPLETE:
  130. SendStatus(pSP);
  131. hr = pSP->SendData(L"pass 2 complete", L"", &lRet);
  132. break;
  133. case PE_EXIT:
  134. SendStatus(pSP);
  135. hr = pSP->SendData(L"build complete", L"", &lRet);
  136. return TRUE;
  137. break;
  138. }
  139. if (hr)
  140. {
  141. BuildErrorRaw("\nBUILD: Communication with script engine failed: %x", hr);
  142. }
  143. return (hr) ? TRUE : FALSE;
  144. }
  145. const DWORD UPDATE_INTERVAL = 2 * 1000; // Update every 2 seconds
  146. //+---------------------------------------------------------------------------
  147. //
  148. // Function: MTScriptThread
  149. //
  150. // Synopsis: Thread entrypoint. Initializes and then sits around
  151. // handling various events.
  152. //
  153. // Arguments: [pv] -- Not used.
  154. //
  155. //----------------------------------------------------------------------------
  156. DWORD WINAPI
  157. MTScriptThread(LPVOID pv)
  158. {
  159. HRESULT hr;
  160. IScriptedProcess * pSP = NULL;
  161. wchar_t achBuf[100];
  162. MSG msg;
  163. DWORD dwRet;
  164. CProcessSink cps;
  165. BOOL fExit = FALSE;
  166. int cRetries = 0;
  167. BuildMsg("Establishing connection with Script engine...\n");
  168. LogMsg("Establishing connection with Script engine...\n");
  169. // Force Windows to create a message queue for this thread, since we will
  170. // be communicated to via PostThreadMessage.
  171. PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
  172. // If anything fails we just quit this thread and communication with
  173. // the MTScript engine won't happen.
  174. hr = CoInitializeEx(NULL, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE);
  175. if (hr)
  176. {
  177. BuildErrorRaw("BUILD: CoInitializeEx failed with %x\n", hr);
  178. goto Cleanup;
  179. }
  180. hr = S_FALSE;
  181. while (hr)
  182. {
  183. pSP = NULL;
  184. IObjectDaemon *pIObjectDaemon;
  185. hr = CoCreateInstance(CLSID_ObjectDaemon, NULL, CLSCTX_SERVER,
  186. IID_IObjectDaemon, (LPVOID*)&pIObjectDaemon);
  187. if (!hr)
  188. {
  189. IDispatch *pIDispatch;
  190. BSTR bstrProgID = SysAllocString(L"MTScript.Remote");
  191. BSTR bstrIdentity = SysAllocString(_wgetenv(L"__MTSCRIPT_ENV_IDENTITY"));
  192. hr = pIObjectDaemon->OpenInterface(bstrIdentity, bstrProgID, (BOOL)FALSE, (IDispatch**)&pIDispatch);
  193. if (!hr)
  194. {
  195. IConnectedMachine *pIConnectedMachine;
  196. hr = pIDispatch->QueryInterface(IID_IConnectedMachine, (LPVOID*)&pIConnectedMachine);
  197. if (!hr)
  198. {
  199. hr = pIConnectedMachine->CreateIScriptedProcess(GetCurrentProcessId(), (wchar_t *)_wgetenv(L"__MTSCRIPT_ENV_ID"), (IScriptedProcess **)&pSP);
  200. pIConnectedMachine->Release();
  201. }
  202. else
  203. {
  204. BuildMsg("CreateIScriptedProcess failed with %x.\n", hr);
  205. LogMsg("CreateIScriptedProcess failed with %x.\n", hr);
  206. }
  207. pIDispatch->Release();
  208. }
  209. else
  210. {
  211. BuildMsg("OpenInterface failed with %x.\n", hr);
  212. LogMsg("OpenInterface failed with %x.\n", hr);
  213. }
  214. SysFreeString(bstrProgID);
  215. SysFreeString(bstrIdentity);
  216. pIObjectDaemon->Release();
  217. }
  218. else
  219. {
  220. BuildMsg("CoCreateInstance failed with %x.\n", hr);
  221. LogMsg("CoCreateInstance failed with %x.\n", hr);
  222. }
  223. if (!hr)
  224. {
  225. hr = pSP->SetProcessSink(&cps);
  226. if (hr)
  227. {
  228. BuildMsg("SetProcessSink failed with %x.\n", hr);
  229. LogMsg("SetProcessSink failed with %x.\n", hr);
  230. }
  231. }
  232. if (hr)
  233. {
  234. if (cRetries >= MAX_RETRIES)
  235. {
  236. BuildErrorRaw("BUILD: FATAL: Connection to script engine could not be established. (%x)\n", hr);
  237. goto Cleanup;
  238. }
  239. if (pSP)
  240. {
  241. pSP->Release();
  242. pSP = NULL;
  243. }
  244. BuildMsg("Connection to script engine failed with %x, retries=%d...\n", hr, cRetries);
  245. LogMsg("Connection to script engine failed with %x, retries=%d...\n", hr, cRetries);
  246. Sleep(500);
  247. cRetries++;
  248. }
  249. }
  250. BuildMsg("Connection to script engine established...\n");
  251. LogMsg("Connection to script engine established...\r\n");
  252. // Tell build.c that it can continue
  253. SetEvent(g_hMTEvent);
  254. while (TRUE)
  255. {
  256. while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
  257. {
  258. if (HandleMessage(&msg, pSP))
  259. {
  260. fExit = TRUE;
  261. }
  262. }
  263. if (fExit)
  264. {
  265. break;
  266. }
  267. dwRet = MsgWaitForMultipleObjects(0,
  268. NULL,
  269. FALSE,
  270. UPDATE_INTERVAL,
  271. QS_ALLINPUT);
  272. if (dwRet == WAIT_OBJECT_0)
  273. {
  274. // A message is coming through on our message queue. Just loop
  275. // around.
  276. }
  277. else if (dwRet == WAIT_TIMEOUT)
  278. {
  279. SendStatus(pSP);
  280. }
  281. else
  282. {
  283. // MWFMO failed. Just bail out.
  284. break;
  285. }
  286. }
  287. Cleanup:
  288. if (pSP)
  289. {
  290. pSP->SetProcessSink(NULL);
  291. pSP->Release();
  292. }
  293. CoUninitialize();
  294. if (hr)
  295. {
  296. g_hMTThread = NULL;
  297. }
  298. SetEvent(g_hMTEvent);
  299. return 0;
  300. }
  301. // ***********************************************************************
  302. //
  303. // CProcessSink implementation
  304. //
  305. // We hand this class to the MTScript engine so it can communicate back
  306. // to us.
  307. //
  308. // ***********************************************************************
  309. CProcessSink::CProcessSink()
  310. {
  311. _ulRefs = 1;
  312. }
  313. HRESULT
  314. CProcessSink::QueryInterface(REFIID riid, LPVOID *ppv)
  315. {
  316. if (riid == IID_IUnknown || riid == IID_IScriptedProcessSink)
  317. {
  318. *ppv = (IScriptedProcessSink*)this;
  319. }
  320. else
  321. {
  322. *ppv = NULL;
  323. return E_NOINTERFACE;
  324. }
  325. ((IUnknown *)*ppv)->AddRef();
  326. return S_OK;
  327. }
  328. ULONG
  329. CProcessSink::AddRef()
  330. {
  331. return InterlockedIncrement((long*)&_ulRefs);
  332. }
  333. ULONG
  334. CProcessSink::Release()
  335. {
  336. if (InterlockedDecrement((long*)&_ulRefs) == 0)
  337. {
  338. _ulRefs = 0xFF;
  339. delete this;
  340. return 0;
  341. }
  342. return _ulRefs;
  343. }
  344. //+---------------------------------------------------------------------------
  345. //
  346. // Member: CProcessSink::RequestExit, public
  347. //
  348. // Synopsis: Called when the MTScript engine wants us to quit. If we don't,
  349. // it will terminate us.
  350. //
  351. //----------------------------------------------------------------------------
  352. HRESULT
  353. CProcessSink::RequestExit()
  354. {
  355. // There is no easy way to tell build.exe to abort. We'll just let
  356. // MTScript terminate us.
  357. return S_OK;
  358. }
  359. //+---------------------------------------------------------------------------
  360. //
  361. // Member: CProcessSink::ReceiveData, public
  362. //
  363. // Synopsis: Called when the MTScript engine wants to send us a message.
  364. //
  365. // Arguments: [pszType] -- String giving the message
  366. // [pszData] -- String giving data associated with the message.
  367. // [plReturn] -- A place we can return a value back.
  368. //
  369. //----------------------------------------------------------------------------
  370. HRESULT
  371. CProcessSink::ReceiveData(wchar_t *pszType, wchar_t *pszData, long *plReturn)
  372. {
  373. *plReturn = 0;
  374. if (wcscmp(pszType, L"resume") == 0)
  375. {
  376. SetEvent(g_hMTEvent);
  377. }
  378. else
  379. {
  380. *plReturn = -1; // Signals an error
  381. }
  382. return S_OK;
  383. }