windows-nt/Source/XPSP1/NT/sdktools/build/buildscr.cpp
2020-09-26 16:20:57 +08:00

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;
}