444 lines
13 KiB
C++
444 lines
13 KiB
C++
|
// manifestmanlger.cpp : Defines the entry point for the console application.
|
||
|
//
|
||
|
|
||
|
#include "stdinc.h"
|
||
|
#include "atlbase.h"
|
||
|
|
||
|
IXMLDOMDocument *g_pCurrentDocument;
|
||
|
BOOL g_bIgnoreMalformedXML = FALSE;
|
||
|
|
||
|
#define MAX_OPERATIONS (20)
|
||
|
|
||
|
extern const string ASM_NAMESPACE_URI ("urn:schemas-microsoft-com:asm.v1");
|
||
|
|
||
|
char MicrosoftCopyrightLogo[] = "Microsoft (R) Side-By-Side Manifest Tool 1.0.0.0\nCopyright (C) Microsoft Corporation 2000-2001. All Rights Reserved.\n\n";
|
||
|
bool g_RazzleBuildTime = false;
|
||
|
|
||
|
//
|
||
|
// Global flags used in processing
|
||
|
//
|
||
|
string g_BinplaceLogFile;
|
||
|
string g_CdfOutputPath;
|
||
|
ATL::CComPtr<IClassFactory> g_XmlDomClassFactory;
|
||
|
bool g_SuppressBanner = false;
|
||
|
|
||
|
#define XMLDOMSOURCE_FILE (1)
|
||
|
#define XMLDOMSOURCE_STRING (2)
|
||
|
#define MAX_ARGUMENTS (500)
|
||
|
#define MAX_ARGUMENT_LENGTH (8192)
|
||
|
|
||
|
bool
|
||
|
InitializeMSXML3()
|
||
|
{
|
||
|
static HMODULE hMsXml3 = NULL;
|
||
|
ATL::CComPtr<IClassFactory> pFactory;
|
||
|
HRESULT hr;
|
||
|
typedef HRESULT (__stdcall * PFN_DLL_GET_CLASS_OBJECT)(REFCLSID, REFIID, LPVOID*);
|
||
|
PFN_DLL_GET_CLASS_OBJECT pfnGetClassObject = NULL;
|
||
|
|
||
|
if (hMsXml3 == NULL)
|
||
|
{
|
||
|
hMsXml3 = LoadLibraryA("msxml3.dll");
|
||
|
if (hMsXml3 == NULL)
|
||
|
{
|
||
|
cerr << "Unable to load msxml3, trying msxml2" << endl;
|
||
|
hMsXml3 = LoadLibraryA("msxml2.dll");
|
||
|
if (hMsXml3 == NULL)
|
||
|
{
|
||
|
cerr << "Unable to load msxml2, trying msxml" << endl;
|
||
|
hMsXml3 = LoadLibraryA("msxml.dll");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (hMsXml3 == NULL) {
|
||
|
cerr << "Very Bad Things - no msxml exists on this machine?" << endl;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
pfnGetClassObject = (PFN_DLL_GET_CLASS_OBJECT)GetProcAddress(hMsXml3, "DllGetClassObject");
|
||
|
if (!pfnGetClassObject)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
hr = pfnGetClassObject(__uuidof(MSXML2::DOMDocument30), __uuidof(pFactory), (void**)&pFactory);
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
cerr << "Can't load version 3.0, trying 2.6" << endl;
|
||
|
|
||
|
hr = pfnGetClassObject(__uuidof(MSXML2::DOMDocument26), __uuidof(pFactory), (void**)&pFactory);
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
cerr << "Can't load version 2.6, trying 1.0" << endl;
|
||
|
|
||
|
// from msxml.h, not msxml2.h
|
||
|
// hr = pfnGetClassObject(__uuidof(DOMDocument), __uuidof(pFactory), (void**)&pFactory);
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
cerr << "Poked: no XML v1.0" << endl;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
g_XmlDomClassFactory = pFactory;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
HRESULT
|
||
|
ConstructXMLDOMObject(
|
||
|
string SourceName,
|
||
|
ATL::CComPtr<IXMLDOMDocument> &document
|
||
|
)
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
VARIANT_BOOL vb;
|
||
|
|
||
|
hr = g_XmlDomClassFactory->CreateInstance(NULL, __uuidof(document), (void**)&document);
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If they're willing to deal with bad XML, then so be it.
|
||
|
//
|
||
|
if (FAILED(hr = document->put_validateOnParse(VARIANT_FALSE)))
|
||
|
{
|
||
|
stringstream ss;
|
||
|
ss << "MSXMLDOM Refuses to be let the wool be pulled over its eyes!";
|
||
|
ReportError(ErrorSpew, ss);
|
||
|
}
|
||
|
|
||
|
hr = document->put_preserveWhiteSpace(VARIANT_TRUE);
|
||
|
hr = document->put_resolveExternals(VARIANT_FALSE);
|
||
|
|
||
|
CFileStreamBase *fsbase = new CFileStreamBase; // LEAK out of paranoia
|
||
|
ATL::CComPtr<IStream> istream = fsbase;
|
||
|
|
||
|
if (!fsbase->OpenForRead(SourceName))
|
||
|
{
|
||
|
stringstream ss;
|
||
|
ss << "Failed opening " << SourceName.c_str() << " for read.";
|
||
|
ReportError(ErrorFatal, ss);
|
||
|
|
||
|
return E_FAIL;
|
||
|
}
|
||
|
|
||
|
hr = document->load(_variant_t(istream), &vb);
|
||
|
if (vb != VARIANT_TRUE)
|
||
|
{
|
||
|
ATL::CComPtr<IXMLDOMParseError> perror;
|
||
|
hr = document->get_parseError(&perror);
|
||
|
long ecode, filepos, linenumber, linepos;
|
||
|
BSTR reason, src;
|
||
|
|
||
|
perror->get_errorCode(&ecode);
|
||
|
perror->get_filepos(&filepos);
|
||
|
perror->get_line(&linenumber);
|
||
|
perror->get_linepos(&linepos);
|
||
|
perror->get_reason(&reason);
|
||
|
perror->get_srcText(&src);
|
||
|
|
||
|
stringstream ss;
|
||
|
ss << "Error: " << hex << ecode << dec << " " << (char*)_bstr_t(reason)
|
||
|
<< " at position " << filepos << ", line " << linenumber << " column " << linepos << endl
|
||
|
<< " the text was: " << endl << (char*)_bstr_t(src) << endl;
|
||
|
ReportError(ErrorFatal, ss);
|
||
|
|
||
|
hr = E_FAIL;
|
||
|
}
|
||
|
|
||
|
fsbase->Close();
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
void dispUsage()
|
||
|
{
|
||
|
const char HelpMessage[] =
|
||
|
"Modes of operation:\n"
|
||
|
" -hashupdate Update hashes of member files\n"
|
||
|
" -makecdfs Generate CDF files to make catalogs\n"
|
||
|
" -verbose Disply piles of debugging information\n"
|
||
|
"\n"
|
||
|
"Modifiers:\n"
|
||
|
" -manifest <foo.man> The name of the manifest to work with\n"
|
||
|
"\n"
|
||
|
"Normal usage:"
|
||
|
" mt.exe -hashupdate -makecdfs -manifest foo.man\n"
|
||
|
"\n";
|
||
|
|
||
|
cout << HelpMessage;
|
||
|
|
||
|
}
|
||
|
|
||
|
BOOL
|
||
|
ProcessParameters(
|
||
|
const vector<wstring> ¶ms,
|
||
|
bool &bUpdateHashes,
|
||
|
bool &bCreateCatalogs,
|
||
|
bool &bVerbosity,
|
||
|
pair<bool, CPostbuildProcessListEntry> &bCmdLineSingleItem
|
||
|
)
|
||
|
{
|
||
|
std::vector<wstring>::const_iterator ci;
|
||
|
bUpdateHashes = bCreateCatalogs = bVerbosity = false;
|
||
|
|
||
|
for (ci = params.begin(); ci != params.end(); ci++)
|
||
|
{
|
||
|
if (*ci == wstring(L"-?"))
|
||
|
{
|
||
|
dispUsage();
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
bUpdateHashes |= (*ci == wstring(L"-hashupdate"));
|
||
|
bCreateCatalogs |= (*ci == wstring(L"-makecdfs"));
|
||
|
bVerbosity |= (*ci == wstring(L"-verbose"));
|
||
|
|
||
|
if (*ci == wstring(L"-binplacelog"))
|
||
|
{
|
||
|
g_BinplaceLogFile = SwitchStringRep(*++ci);
|
||
|
}
|
||
|
else if (*ci == wstring(L"-razzle"))
|
||
|
{
|
||
|
g_RazzleBuildTime = true;
|
||
|
}
|
||
|
else if (*ci == wstring(L"-cdfpath"))
|
||
|
{
|
||
|
std::vector<wstring>::const_iterator cinext = ci + 1;
|
||
|
if ( ( cinext == params.end() ) || ( (*cinext).at(0) == L'-' ) )
|
||
|
{
|
||
|
stringstream ss;
|
||
|
ss << "-cdfpath needs a path name";
|
||
|
ReportError( ErrorFatal, ss );
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
g_CdfOutputPath = SwitchStringRep(*++ci);
|
||
|
}
|
||
|
else if (*ci == wstring(L"-asmsroot"))
|
||
|
{
|
||
|
std::vector<wstring>::const_iterator cinext = ci + 1;
|
||
|
if ( ( cinext == params.end() ) || ( (*cinext).at(0) == L'-' ) )
|
||
|
{
|
||
|
stringstream ss;
|
||
|
ss << "-asmsroot needs a path name";
|
||
|
ReportError( ErrorFatal, ss );
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
g_AsmsBuildRootPath = SwitchStringRep(*++ci);
|
||
|
}
|
||
|
else if (*ci == wstring(L"-manifest"))
|
||
|
{
|
||
|
std::vector<wstring>::const_iterator cinext = ci + 1;
|
||
|
if ( ( cinext == params.end() ) || ( (*cinext).at(0) == L'-' ) )
|
||
|
{
|
||
|
stringstream ss;
|
||
|
ss << "-manifest needs a file name";
|
||
|
ReportError( ErrorFatal, ss );
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
bCmdLineSingleItem.first = true;
|
||
|
|
||
|
CHAR ch[MAX_PATH];
|
||
|
GetCurrentDirectoryA(MAX_PATH, ch);
|
||
|
|
||
|
bCmdLineSingleItem.first = true;
|
||
|
bCmdLineSingleItem.second.setManifestLocation(string(ch), SwitchStringRep(*++ci));
|
||
|
}
|
||
|
else if (*ci == wstring(L"-version"))
|
||
|
{
|
||
|
std::vector<wstring>::const_iterator cinext = ci + 1;
|
||
|
if ( ( cinext == params.end() ) || ( (*cinext).at(0) == L'-' ) )
|
||
|
{
|
||
|
stringstream ss;
|
||
|
ss << "-version needs a version string";
|
||
|
ReportError( ErrorFatal, ss );
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
bCmdLineSingleItem.first = true;
|
||
|
bCmdLineSingleItem.second.version = SwitchStringRep(*++ci);
|
||
|
}
|
||
|
else if (*ci == wstring(L"-language"))
|
||
|
{
|
||
|
std::vector<wstring>::const_iterator cinext = ci + 1;
|
||
|
if ( ( cinext == params.end() ) || ( (*cinext).at(0) == L'-' ) )
|
||
|
{
|
||
|
stringstream ss;
|
||
|
ss << "-language needs a culture name string";
|
||
|
ReportError( ErrorFatal, ss );
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
bCmdLineSingleItem.first = true;
|
||
|
bCmdLineSingleItem.second.language = JustifyPath(SwitchStringRep(*++ci));
|
||
|
}
|
||
|
else if (*ci == wstring(L"-name"))
|
||
|
{
|
||
|
std::vector<wstring>::const_iterator cinext = ci + 1;
|
||
|
if ( ( cinext == params.end() ) || ( (*cinext).at(0) == L'-' ) )
|
||
|
{
|
||
|
stringstream ss;
|
||
|
ss << "-name needs an assembly name string";
|
||
|
ReportError( ErrorFatal, ss );
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
bCmdLineSingleItem.first = true;
|
||
|
bCmdLineSingleItem.second.name = SwitchStringRep(*++ci);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
int __cdecl wmain(int argc, WCHAR* argv[])
|
||
|
{
|
||
|
using namespace std;
|
||
|
|
||
|
bool bUpdateHashes = true;
|
||
|
bool bGenerateCatalogs = true;
|
||
|
vector<wstring> params;
|
||
|
HRESULT hr;
|
||
|
pair<bool, CPostbuildProcessListEntry> hasInlinedManifest;
|
||
|
|
||
|
for (int i = 0; i < argc; i++) {
|
||
|
wstring here = argv[i];
|
||
|
if ( here == wstring(L"-nologo"))
|
||
|
g_SuppressBanner = true;
|
||
|
else
|
||
|
params.push_back(here);
|
||
|
}
|
||
|
|
||
|
if ( !g_SuppressBanner )
|
||
|
{
|
||
|
cout << MicrosoftCopyrightLogo;
|
||
|
}
|
||
|
|
||
|
if (!ProcessParameters(params, bUpdateHashes, bGenerateCatalogs, g_bDisplaySpew, hasInlinedManifest))
|
||
|
return 1;
|
||
|
|
||
|
//
|
||
|
// Start COM
|
||
|
//
|
||
|
if (FAILED(hr = ::CoInitialize(NULL)))
|
||
|
{
|
||
|
stringstream ss;
|
||
|
|
||
|
ss << "Unable to start com, error " << hex << hr;
|
||
|
ReportError(ErrorFatal, ss);
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
if (!InitializeMSXML3())
|
||
|
{
|
||
|
return 2;
|
||
|
}
|
||
|
|
||
|
if (hasInlinedManifest.first)
|
||
|
{
|
||
|
PostbuildEntries.push_back(hasInlinedManifest.second);
|
||
|
goto StartProcessing;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Populate the processing list, but only if we're really in a Razzle
|
||
|
// environment
|
||
|
//
|
||
|
if ( g_RazzleBuildTime )
|
||
|
{
|
||
|
// string chName = convertWCharToAnsi(argv[1]);
|
||
|
ifstream BinplaceLog;
|
||
|
|
||
|
BinplaceLog.open(g_BinplaceLogFile.c_str());
|
||
|
if (!BinplaceLog.is_open()) {
|
||
|
cerr << "Failed opening '" << g_BinplaceLogFile << "' as the binplace log?" << endl;
|
||
|
cerr << "Ensure that the path passed by '-binplacelog' is valid." << endl;
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
while (!BinplaceLog.eof())
|
||
|
{
|
||
|
string sourceLine;
|
||
|
wstring wSourceLine;
|
||
|
CPostbuildProcessListEntry item;
|
||
|
StringStringMap ValuePairs;
|
||
|
|
||
|
getline(BinplaceLog, sourceLine);
|
||
|
wSourceLine = SwitchStringRep(sourceLine);
|
||
|
|
||
|
ValuePairs = MapFromDefLine(wSourceLine);
|
||
|
if (!ValuePairs.size())
|
||
|
continue;
|
||
|
|
||
|
item.name = SwitchStringRep(ValuePairs[L"SXS_ASSEMBLY_NAME"]);
|
||
|
item.version = SwitchStringRep(ValuePairs[L"SXS_ASSEMBLY_VERSION"]);
|
||
|
item.language = SwitchStringRep(ValuePairs[L"SXS_ASSEMBLY_LANGUAGE"]);
|
||
|
item.setManifestLocation(g_AsmsBuildRootPath, SwitchStringRep(ValuePairs[L"SXS_MANIFEST"]));
|
||
|
|
||
|
if (item.getManifestFullPath().find("asms\\") == -1) {
|
||
|
stringstream ss;
|
||
|
ss << "Skipping manifested item " << item << " because it's not under asms.";
|
||
|
ReportError(ErrorSpew, ss);
|
||
|
} else {
|
||
|
PostbuildEntries.push_back(item);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
std::sort(PostbuildEntries.begin(), PostbuildEntries.end());
|
||
|
PostbuildEntries.resize(std::unique(PostbuildEntries.begin(), PostbuildEntries.end()) - PostbuildEntries.begin());
|
||
|
}
|
||
|
else if ( !hasInlinedManifest.first )
|
||
|
{
|
||
|
//
|
||
|
// No -razzle and no -manifest? Whoops...
|
||
|
//
|
||
|
dispUsage();
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
StartProcessing:
|
||
|
|
||
|
if ( !bUpdateHashes && !bGenerateCatalogs )
|
||
|
{
|
||
|
cout << "Nothing to do!" << endl;
|
||
|
dispUsage();
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
for (vector<CPostbuildProcessListEntry>::const_iterator cursor = PostbuildEntries.begin(); cursor != PostbuildEntries.end(); cursor++)
|
||
|
{
|
||
|
//
|
||
|
// First, mash the hashes around.
|
||
|
//
|
||
|
if (bUpdateHashes)
|
||
|
UpdateManifestHashes(*cursor);
|
||
|
|
||
|
//
|
||
|
// Second, generate catalogs
|
||
|
//
|
||
|
if (bGenerateCatalogs)
|
||
|
GenerateCatalogContents(*cursor);
|
||
|
}
|
||
|
|
||
|
hr = S_OK;
|
||
|
|
||
|
return (hr == S_OK) ? 0 : 1;
|
||
|
}
|
||
|
|
||
|
|
||
|
CPostbuildItemVector PostbuildEntries;
|