468 lines
12 KiB
C++
468 lines
12 KiB
C++
//+---------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1992 - 1995
|
|
//
|
|
// File: buildscr.cpp
|
|
//
|
|
// Contents: Implementation of the code which talks to the MTScript engine
|
|
// when doing a distributed build using the build console.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#include "scrproc.h"
|
|
|
|
#include "build.h"
|
|
#include "buildscr.h"
|
|
|
|
#define INITGUID
|
|
#include <guiddef.h>
|
|
|
|
DEFINE_GUID(CLSID_LocalScriptedProcess, 0x854c316f,0xc854,0x4a77,0xb1,0x89,0x60,0x68,0x59,0xe4,0x39,0x1b);
|
|
DEFINE_GUID(IID_IScriptedProcess, 0x854c3171,0xc854,0x4a77,0xb1,0x89,0x60,0x68,0x59,0xe4,0x39,0x1b);
|
|
DEFINE_GUID(IID_IScriptedProcessSink, 0x854c3172,0xc854,0x4a77,0xb1,0x89,0x60,0x68,0x59,0xe4,0x39,0x1b);
|
|
|
|
DEFINE_GUID(CLSID_ObjectDaemon,0x854c3184,0xc854,0x4a77,0xb1,0x89,0x60,0x68,0x59,0xE4,0x39,0x1b);
|
|
DEFINE_GUID(IID_IConnectedMachine,0x854c316c,0xc854,0x4a77,0xb1,0x89,0x60,0x68,0x59,0xe4,0x39,0x1b);
|
|
DEFINE_GUID(IID_IObjectDaemon,0x854c3183,0xc854,0x4a77,0xb1,0x89,0x60,0x68,0x59,0xE4,0x39,0x1b);
|
|
|
|
#define MAX_RETRIES 2
|
|
|
|
HANDLE g_hMTEvent = NULL;
|
|
HANDLE g_hMTThread = NULL;
|
|
DWORD g_dwMTThreadId = 0;
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: WaitForResume
|
|
//
|
|
// Synopsis: Sends a "phase complete" message to the script engine and then
|
|
// waits for it to tell us to resume (if specified).
|
|
//
|
|
// Arguments: [fPause] -- If TRUE, we wait for a resume command
|
|
// [pe] -- Message to send to the script engine
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void
|
|
WaitForResume(BOOL fPause, PROC_EVENTS pe)
|
|
{
|
|
if (g_hMTEvent)
|
|
{
|
|
HANDLE aHandles[2] = { g_hMTEvent, g_hMTThread };
|
|
|
|
ResetEvent(g_hMTEvent);
|
|
|
|
PostThreadMessage(g_dwMTThreadId, pe, 0, 0);
|
|
|
|
if (fPause)
|
|
{
|
|
// Wait until either the event object is signaled or the thread dies
|
|
WaitForMultipleObjects(2, aHandles, FALSE, INFINITE);
|
|
}
|
|
}
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: ExitMTScriptThread
|
|
//
|
|
// Synopsis: Tells the thread talking to the MTScript engine to exit.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void
|
|
ExitMTScriptThread()
|
|
{
|
|
if (g_hMTEvent)
|
|
{
|
|
PostThreadMessage(g_dwMTThreadId, PE_EXIT, 0, 0);
|
|
|
|
WaitForSingleObject(g_hMTThread, INFINITE);
|
|
|
|
CloseHandle(g_hMTThread);
|
|
CloseHandle(g_hMTEvent);
|
|
}
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: SendStatus
|
|
//
|
|
// Synopsis: Sends a status message to the MTScript engine with the
|
|
// current number of errors, warnings, and completed files.
|
|
//
|
|
// Arguments: [pSP] -- Pointer to MTScript engine interface
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void
|
|
SendStatus(IScriptedProcess *pSP)
|
|
{
|
|
wchar_t achBuf[300];
|
|
long lRet;
|
|
|
|
static ULONG cErrorsPrev = MAXDWORD;
|
|
static ULONG cWarnPrev = MAXDWORD;
|
|
static ULONG cFilesPrev = MAXDWORD;
|
|
|
|
ULONG cErrors = NumberCompileErrors + NumberLibraryErrors + NumberLinkErrors + NumberBinplaceErrors;
|
|
ULONG cWarn = NumberCompileWarnings + NumberLibraryWarnings + NumberLinkWarnings + NumberBinplaceWarnings;
|
|
ULONG cFiles = NumberCompiles + NumberLibraries + NumberLinks /* + NumberBinplaces */;
|
|
|
|
// Only send status if it's changed since last time we did it.
|
|
if ( cErrors != cErrorsPrev
|
|
|| cWarn != cWarnPrev
|
|
|| cFiles != cFilesPrev)
|
|
{
|
|
cErrorsPrev = cErrors;
|
|
cWarnPrev = cWarn;
|
|
cFilesPrev = cFiles;
|
|
|
|
wsprintfW(achBuf, L"errors=%d,warnings=%d,files=%d", cErrors, cWarn, cFiles);
|
|
|
|
pSP->SendData(L"status", achBuf, &lRet);
|
|
}
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: HandleMessage
|
|
//
|
|
// Synopsis: Handles a message that has come across our message queue.
|
|
//
|
|
// Arguments: [pmsg] -- Message
|
|
// [pSP] -- Pointer to MTScript engine interface
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
BOOL
|
|
HandleMessage(MSG *pmsg, IScriptedProcess *pSP)
|
|
{
|
|
long lRet;
|
|
HRESULT hr = S_OK;
|
|
|
|
switch (pmsg->message)
|
|
{
|
|
case PE_PASS0_COMPLETE:
|
|
SendStatus(pSP);
|
|
|
|
hr = pSP->SendData(L"pass 0 complete", L"", &lRet);
|
|
|
|
break;
|
|
|
|
case PE_PASS1_COMPLETE:
|
|
SendStatus(pSP);
|
|
|
|
hr = pSP->SendData(L"pass 1 complete", L"", &lRet);
|
|
|
|
break;
|
|
|
|
case PE_PASS2_COMPLETE:
|
|
SendStatus(pSP);
|
|
|
|
hr = pSP->SendData(L"pass 2 complete", L"", &lRet);
|
|
|
|
break;
|
|
|
|
case PE_EXIT:
|
|
SendStatus(pSP);
|
|
|
|
hr = pSP->SendData(L"build complete", L"", &lRet);
|
|
|
|
return TRUE;
|
|
break;
|
|
}
|
|
|
|
if (hr)
|
|
{
|
|
BuildErrorRaw("\nBUILD: Communication with script engine failed: %x", hr);
|
|
}
|
|
|
|
return (hr) ? TRUE : FALSE;
|
|
}
|
|
|
|
const DWORD UPDATE_INTERVAL = 2 * 1000; // Update every 2 seconds
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: MTScriptThread
|
|
//
|
|
// Synopsis: Thread entrypoint. Initializes and then sits around
|
|
// handling various events.
|
|
//
|
|
// Arguments: [pv] -- Not used.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
DWORD WINAPI
|
|
MTScriptThread(LPVOID pv)
|
|
{
|
|
HRESULT hr;
|
|
IScriptedProcess * pSP = NULL;
|
|
wchar_t achBuf[100];
|
|
MSG msg;
|
|
DWORD dwRet;
|
|
CProcessSink cps;
|
|
BOOL fExit = FALSE;
|
|
int cRetries = 0;
|
|
|
|
BuildMsg("Establishing connection with Script engine...\n");
|
|
LogMsg("Establishing connection with Script engine...\n");
|
|
|
|
// Force Windows to create a message queue for this thread, since we will
|
|
// be communicated to via PostThreadMessage.
|
|
|
|
PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
|
|
|
|
// If anything fails we just quit this thread and communication with
|
|
// the MTScript engine won't happen.
|
|
|
|
hr = CoInitializeEx(NULL, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE);
|
|
if (hr)
|
|
{
|
|
BuildErrorRaw("BUILD: CoInitializeEx failed with %x\n", hr);
|
|
goto Cleanup;
|
|
}
|
|
|
|
hr = S_FALSE;
|
|
|
|
while (hr)
|
|
{
|
|
pSP = NULL;
|
|
IObjectDaemon *pIObjectDaemon;
|
|
hr = CoCreateInstance(CLSID_ObjectDaemon, NULL, CLSCTX_SERVER,
|
|
IID_IObjectDaemon, (LPVOID*)&pIObjectDaemon);
|
|
|
|
if (!hr)
|
|
{
|
|
IDispatch *pIDispatch;
|
|
BSTR bstrProgID = SysAllocString(L"MTScript.Remote");
|
|
BSTR bstrIdentity = SysAllocString(_wgetenv(L"__MTSCRIPT_ENV_IDENTITY"));
|
|
hr = pIObjectDaemon->OpenInterface(bstrIdentity, bstrProgID, (BOOL)FALSE, (IDispatch**)&pIDispatch);
|
|
if (!hr)
|
|
{
|
|
IConnectedMachine *pIConnectedMachine;
|
|
hr = pIDispatch->QueryInterface(IID_IConnectedMachine, (LPVOID*)&pIConnectedMachine);
|
|
if (!hr)
|
|
{
|
|
hr = pIConnectedMachine->CreateIScriptedProcess(GetCurrentProcessId(), (wchar_t *)_wgetenv(L"__MTSCRIPT_ENV_ID"), (IScriptedProcess **)&pSP);
|
|
pIConnectedMachine->Release();
|
|
}
|
|
else
|
|
{
|
|
BuildMsg("CreateIScriptedProcess failed with %x.\n", hr);
|
|
LogMsg("CreateIScriptedProcess failed with %x.\n", hr);
|
|
}
|
|
pIDispatch->Release();
|
|
}
|
|
else
|
|
{
|
|
BuildMsg("OpenInterface failed with %x.\n", hr);
|
|
LogMsg("OpenInterface failed with %x.\n", hr);
|
|
}
|
|
SysFreeString(bstrProgID);
|
|
SysFreeString(bstrIdentity);
|
|
pIObjectDaemon->Release();
|
|
}
|
|
else
|
|
{
|
|
BuildMsg("CoCreateInstance failed with %x.\n", hr);
|
|
LogMsg("CoCreateInstance failed with %x.\n", hr);
|
|
}
|
|
|
|
|
|
if (!hr)
|
|
{
|
|
hr = pSP->SetProcessSink(&cps);
|
|
if (hr)
|
|
{
|
|
BuildMsg("SetProcessSink failed with %x.\n", hr);
|
|
LogMsg("SetProcessSink failed with %x.\n", hr);
|
|
}
|
|
}
|
|
|
|
if (hr)
|
|
{
|
|
if (cRetries >= MAX_RETRIES)
|
|
{
|
|
BuildErrorRaw("BUILD: FATAL: Connection to script engine could not be established. (%x)\n", hr);
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (pSP)
|
|
{
|
|
pSP->Release();
|
|
pSP = NULL;
|
|
}
|
|
|
|
BuildMsg("Connection to script engine failed with %x, retries=%d...\n", hr, cRetries);
|
|
LogMsg("Connection to script engine failed with %x, retries=%d...\n", hr, cRetries);
|
|
|
|
Sleep(500);
|
|
|
|
cRetries++;
|
|
}
|
|
}
|
|
|
|
BuildMsg("Connection to script engine established...\n");
|
|
LogMsg("Connection to script engine established...\r\n");
|
|
|
|
// Tell build.c that it can continue
|
|
SetEvent(g_hMTEvent);
|
|
|
|
while (TRUE)
|
|
{
|
|
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
|
|
{
|
|
if (HandleMessage(&msg, pSP))
|
|
{
|
|
fExit = TRUE;
|
|
}
|
|
}
|
|
|
|
if (fExit)
|
|
{
|
|
break;
|
|
}
|
|
|
|
dwRet = MsgWaitForMultipleObjects(0,
|
|
NULL,
|
|
FALSE,
|
|
UPDATE_INTERVAL,
|
|
QS_ALLINPUT);
|
|
|
|
if (dwRet == WAIT_OBJECT_0)
|
|
{
|
|
// A message is coming through on our message queue. Just loop
|
|
// around.
|
|
}
|
|
else if (dwRet == WAIT_TIMEOUT)
|
|
{
|
|
SendStatus(pSP);
|
|
}
|
|
else
|
|
{
|
|
// MWFMO failed. Just bail out.
|
|
break;
|
|
}
|
|
}
|
|
|
|
Cleanup:
|
|
if (pSP)
|
|
{
|
|
pSP->SetProcessSink(NULL);
|
|
pSP->Release();
|
|
}
|
|
|
|
CoUninitialize();
|
|
|
|
if (hr)
|
|
{
|
|
g_hMTThread = NULL;
|
|
}
|
|
|
|
SetEvent(g_hMTEvent);
|
|
|
|
return 0;
|
|
}
|
|
|
|
// ***********************************************************************
|
|
//
|
|
// CProcessSink implementation
|
|
//
|
|
// We hand this class to the MTScript engine so it can communicate back
|
|
// to us.
|
|
//
|
|
// ***********************************************************************
|
|
|
|
CProcessSink::CProcessSink()
|
|
{
|
|
_ulRefs = 1;
|
|
}
|
|
|
|
HRESULT
|
|
CProcessSink::QueryInterface(REFIID riid, LPVOID *ppv)
|
|
{
|
|
if (riid == IID_IUnknown || riid == IID_IScriptedProcessSink)
|
|
{
|
|
*ppv = (IScriptedProcessSink*)this;
|
|
}
|
|
else
|
|
{
|
|
*ppv = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
((IUnknown *)*ppv)->AddRef();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
ULONG
|
|
CProcessSink::AddRef()
|
|
{
|
|
return InterlockedIncrement((long*)&_ulRefs);
|
|
}
|
|
|
|
ULONG
|
|
CProcessSink::Release()
|
|
{
|
|
if (InterlockedDecrement((long*)&_ulRefs) == 0)
|
|
{
|
|
_ulRefs = 0xFF;
|
|
delete this;
|
|
return 0;
|
|
}
|
|
|
|
return _ulRefs;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CProcessSink::RequestExit, public
|
|
//
|
|
// Synopsis: Called when the MTScript engine wants us to quit. If we don't,
|
|
// it will terminate us.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CProcessSink::RequestExit()
|
|
{
|
|
// There is no easy way to tell build.exe to abort. We'll just let
|
|
// MTScript terminate us.
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CProcessSink::ReceiveData, public
|
|
//
|
|
// Synopsis: Called when the MTScript engine wants to send us a message.
|
|
//
|
|
// Arguments: [pszType] -- String giving the message
|
|
// [pszData] -- String giving data associated with the message.
|
|
// [plReturn] -- A place we can return a value back.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CProcessSink::ReceiveData(wchar_t *pszType, wchar_t *pszData, long *plReturn)
|
|
{
|
|
*plReturn = 0;
|
|
|
|
if (wcscmp(pszType, L"resume") == 0)
|
|
{
|
|
SetEvent(g_hMTEvent);
|
|
}
|
|
else
|
|
{
|
|
*plReturn = -1; // Signals an error
|
|
}
|
|
|
|
return S_OK;
|
|
}
|