/************************************************************************
*
* Description : CProjectLintAnalyser - class for analysing Visual C++
* projects (.vcproj or .dsp) using the PC-Lint code analysis
* tool.
*
* (c) Copyright 2004-2008 by Anna-Jayne Metcalfe (anna@riverblade.co.uk)
* and Beth Mackenzie (beth@riverblade.co.uk) / Riverblade Limited
*
* Licence Terms:
*
* This code may be freely reused, subject to the licence terms below.
* Please do let us know of any bugs you find or improvements you make,
* so that we can pass them on to the rest of the development community.
*
* This code is free software; you can redistribute it and/or
* modify it under the terms of the Code Project Open License (CPOL)
* version 1.0 (http://www.codeproject.com/info/cpol10.aspx).
*
* This code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* Code Project Open Licence for further details.
*
************************************************************************
* $Archive: /Projects/Applications/LintProject/1.4/ProjectLintAnalyser.cpp $
* $Revision: 30 $
* $Date: 7/01/09 12:53 $
* $Author: Anna $
*
* $Nokeywords: $
************************************************************************/
/// \file
/// \brief CProjectLintAnalyser class implementation.
#include "StdAfx.h"
#include "Shared/ModuleVersion.h"
#include "Shared/StringUtils.h"
#include "Shared/PathUtils.h"
#include "Shared/FileUtils.h"
#include "Shared/ResourceUtils.h"
#include "Shared/BrowserUtils.h"
#include "Shared/SplitPath.h"
#include "Shared/ProjectFileReader.h"
#include "Resource.h"
#include "SolutionLintAnalyser.h"
#include "FileLintAnalyser.h"
#include "ProjectLintAnalyser.h"
using namespace std;
CProjectLintAnalyser::CProjectLintAnalyser(void)
: m_sLintFolder(_T("") ),
m_sProjectFilePathName(_T("") ),
m_sProjectFileFolder(_T("") ),
m_sSolutionFileFolder(_T("") ), // [Alex McCarthy 21.10.2008]
m_sPlatformName(_T("") ), // [Alex McCarthy 23.12.2008]
m_sLintConfigFile(_T("") ),
m_sAdditionalProjectParams(_T("") ),
m_sAdditionalParams(_T("") ),
m_sConfiguration(_T("") ),
m_sConfigurationString(_T("") ),
m_sIntermediateFilesFolder(_T("") ),
m_bShowHtml(false),
m_sSolutionFileResultFolder(_T("") ),
m_sProjectName(_T("") ),
m_sResultsFileFolder(_T("") ),
m_timeStarted(),
m_nWarningCount(0),
m_sResultsIndexFilePathName(_T("") ),
m_sElapsedTime(_T("") ),
m_arrayImplementationFilePathNames(),
m_arrayConfigurations(),
m_arrayFileResults(),
m_bAnalysed(false),
m_pSolutionAnalyser(NULL) // Pointer to the containing solution analyser. Only needed for WriteIndexFile() callbacks.
{
m_timeStarted.SetStatus(COleDateTime::null);
}
CProjectLintAnalyser::CProjectLintAnalyser(const CProjectLintAnalyser& src)
: m_sLintFolder(_T("") ),
m_sProjectFilePathName(_T("") ),
m_sProjectFileFolder(_T("") ),
m_sSolutionFileFolder(_T("") ), // [Alex McCarthy 21.10.2008]
m_sPlatformName(_T("") ), // [Alex McCarthy 23.12.2008]
m_sLintConfigFile(_T("") ),
m_sAdditionalProjectParams(_T("") ),
m_sAdditionalParams(_T("") ),
m_sConfiguration(_T("") ),
m_sConfigurationString(_T("") ),
m_sIntermediateFilesFolder(_T("") ),
m_bShowHtml(false),
m_sSolutionFileResultFolder(_T("") ),
m_sProjectName(_T("") ),
m_sResultsFileFolder(_T("") ),
m_timeStarted(), //lint !e747 (Information -- Significant prototype coercion (arg. no. 1) int to long long)
m_nWarningCount(0),
m_sResultsIndexFilePathName(_T("") ),
m_sElapsedTime(_T("") ),
m_arrayImplementationFilePathNames(),
m_arrayConfigurations(),
m_arrayFileResults(),
m_bAnalysed(false),
m_pSolutionAnalyser(NULL) // Pointer to the containing solution analyser. Only needed for WriteIndexFile() callbacks.
{
*this = src;
}
CProjectLintAnalyser::CProjectLintAnalyser( CSolutionLintAnalyser* pSolutionAnalyser,
const CString& sProjectFilePathName,
const CString& sSolutionFileResultFolder,
const CString& sResultsFileFolder,
const CString& sSolutionFileFolder, // [Alex McCarthy 21.10.2008]
const CString& sLintConfigFile,
const CString& sAdditionalProjectParams,
const CString& sAdditionalParams,
const CString& sConfiguration)
: m_sLintFolder(_T("") ),
m_sProjectFilePathName(sProjectFilePathName),
m_sProjectFileFolder(_T("") ),
m_sSolutionFileFolder(sSolutionFileFolder), // [Alex McCarthy 21.10.2008]
m_sPlatformName(_T("") ), // [Alex McCarthy 23.12.2008]
m_sLintConfigFile(sLintConfigFile),
m_sAdditionalProjectParams(sAdditionalProjectParams),
m_sAdditionalParams(sAdditionalParams),
m_sConfiguration(sConfiguration),
m_sConfigurationString(_T("") ),
m_sIntermediateFilesFolder(_T("") ),
m_bShowHtml(false),
m_sSolutionFileResultFolder(sSolutionFileResultFolder),
m_sProjectName(_T("") ),
m_sResultsFileFolder(sResultsFileFolder),
m_timeStarted(),
m_nWarningCount(0),
m_sResultsIndexFilePathName(_T("") ),
m_sElapsedTime(_T("") ),
m_arrayImplementationFilePathNames(),
m_arrayConfigurations(),
m_arrayFileResults(),
m_bAnalysed(false),
m_pSolutionAnalyser(pSolutionAnalyser) // Pointer to the containing solution analyser. Only needed for WriteIndexFile() callbacks.
{
m_timeStarted.SetStatus(COleDateTime::null);
m_sResultsIndexFilePathName = m_sResultsFileFolder + _T("\\index.html");
// Strip leading/trailing quotes - just in case
m_sProjectFilePathName.TrimLeft( _T("\"") );
m_sProjectFilePathName.TrimRight( _T("\"") );
m_sResultsFileFolder.TrimLeft( _T("\"") );
m_sResultsFileFolder.TrimRight( _T("\"") );
Utils::CSplitPath splitproject(m_sProjectFilePathName);
m_sProjectFileFolder = splitproject.GetDrive() + splitproject.GetDirectory();
m_sProjectName = splitproject.GetFileName();
(void)ReadProjectFile( m_sProjectFilePathName,
m_sConfiguration,
m_arrayImplementationFilePathNames,
m_sConfigurationString,
m_sIntermediateFilesFolder);
if (!m_sConfiguration.IsEmpty() )
{
// TODO: Check configuration was found
// [Alex McCarthy 23.12.2008] for VS2002 onwards, the configuration string will be
// of the form CONFIG|PLATFORM
const int nPos = m_sConfiguration.Find(_T("|") );
if (nPos >= 0)
{
m_sPlatformName = m_sConfiguration.Mid(nPos + 1);
}
}
}
CProjectLintAnalyser::~CProjectLintAnalyser(void)
{
m_pSolutionAnalyser = NULL;
}
bool CProjectLintAnalyser::operator==(const CProjectLintAnalyser& src) const
{
if ( (m_sLintFolder == src.m_sLintFolder) &&
(m_sProjectFilePathName == src.m_sProjectFilePathName) &&
(m_sProjectFileFolder == src.m_sProjectFileFolder) &&
(m_sSolutionFileFolder == src.m_sSolutionFileFolder) && // [Alex McCarthy 21.10.2008]
(m_sPlatformName == src.m_sPlatformName) && // [Alex McCarthy 23.12.2008]
(m_sLintConfigFile == m_sLintConfigFile) &&
(m_sAdditionalProjectParams == src.m_sAdditionalProjectParams) &&
(m_sAdditionalParams == src.m_sAdditionalParams) &&
(m_sConfiguration == src.m_sConfiguration) &&
(m_sConfigurationString == src.m_sConfigurationString) &&
(m_sIntermediateFilesFolder == src.m_sIntermediateFilesFolder) &&
(m_bShowHtml == src.m_bShowHtml) &&
(m_sSolutionFileResultFolder == src.m_sSolutionFileResultFolder) &&
(m_sProjectName == src.m_sProjectName) &&
(m_sResultsFileFolder == src.m_sResultsFileFolder) &&
(m_timeStarted == src.m_timeStarted) &&
(m_nWarningCount == src.m_nWarningCount) &&
(m_sResultsIndexFilePathName == src.m_sResultsIndexFilePathName) &&
(m_sElapsedTime == src.m_sElapsedTime) &&
(m_bAnalysed == src.m_bAnalysed) &&
(m_pSolutionAnalyser == src.m_pSolutionAnalyser) )
{
// NB m_arrayImplementationFilePathNames, m_arrayConfigurations and m_arrayFileResults are not currently comparsed
return true;
}
return false;
}
CProjectLintAnalyser& CProjectLintAnalyser::operator=(const CProjectLintAnalyser& src)
{
if (this != &src)
{
m_pSolutionAnalyser = src.m_pSolutionAnalyser; //lint !e1555 (Warning -- Direct pointer copy of member within copy assignment operator: 'CProjectLintAnalyser::operator=(const CProjectLintAnalyser &)')
// (there is only ever one CSolutionLintAnalyser object, so this is (sort of) OK.
m_sLintFolder = src.m_sLintFolder;
m_bShowHtml = src.m_bShowHtml;
m_sSolutionFileResultFolder = src.m_sSolutionFileResultFolder;
m_sLintConfigFile = src.m_sLintConfigFile;
m_sAdditionalProjectParams = src.m_sAdditionalProjectParams;
m_sAdditionalParams = src.m_sAdditionalParams;
m_sProjectFilePathName = src.m_sProjectFilePathName;
m_sProjectFileFolder = src.m_sProjectFileFolder;
m_sProjectName = src.m_sProjectName;
m_sSolutionFileFolder = src.m_sSolutionFileFolder; // [Alex McCarthy 21.10.2008]
m_sPlatformName = src.m_sPlatformName; // [Alex McCarthy 23.12.2008]
m_sConfiguration = src.m_sConfiguration;
m_sConfigurationString = src.m_sConfigurationString;
m_sIntermediateFilesFolder = src.m_sIntermediateFilesFolder;
m_sResultsFileFolder = src.m_sResultsFileFolder;
m_sResultsIndexFilePathName = src.m_sResultsIndexFilePathName;
m_timeStarted = src.m_timeStarted;
m_nWarningCount = src.m_nWarningCount;
m_sElapsedTime = src.m_sElapsedTime;
m_bAnalysed = src.m_bAnalysed;
for (size_t k = 0; k < src.m_arrayConfigurations.GetCount(); k++)
{
(void)m_arrayConfigurations.Add(src.m_arrayConfigurations[k] );
}
for (size_t m = 0; m < src.m_arrayImplementationFilePathNames.GetCount(); m++)
{
(void)m_arrayImplementationFilePathNames.Add(src.m_arrayImplementationFilePathNames[m] );
}
for (size_t n = 0; n < src.m_arrayFileResults.GetCount(); n++)
{
m_arrayFileResults.Add(src.m_arrayFileResults[n] ); // (const CFileLintAnalyser&)
}
}
return *this;
}
bool CProjectLintAnalyser::IsVSDotNetProject(void) const
{
Utils::CSplitPath split(m_sProjectFilePathName);
CString sExtension = split.GetExtension();
sExtension.MakeLower();
return ( _T(".vcproj") == sExtension);
}
bool CProjectLintAnalyser::SetLintFolder(const CString& sFolder)
{
m_sLintFolder = sFolder;
ATLASSERT(!m_sLintFolder.IsEmpty() );
return !m_sLintFolder.IsEmpty();
}
bool CProjectLintAnalyser::ShowHtml(const bool bShowHtml)
{
m_bShowHtml = bShowHtml;
return true;
}
size_t CProjectLintAnalyser::GetConfigurations(CAtlArray<CString>& rarrayConfigurations) const
{
rarrayConfigurations.RemoveAll();
(void)rarrayConfigurations.Append(m_arrayConfigurations);
return rarrayConfigurations.GetCount();
}
int CProjectLintAnalyser::GetWarningCount(void) const
{
return m_nWarningCount;
}
CString CProjectLintAnalyser::GetResultsIndexFilePathName(void) const
{
return m_sResultsIndexFilePathName;
}
CString CProjectLintAnalyser::GetResultsIndexFileName(void) const
{
const int nPos = m_sResultsIndexFilePathName.ReverseFind( _T('\\') );
ATLASSERT(nPos > 0);
if (nPos > 0)
{
CString sResultsIndexFileName = m_sResultsIndexFilePathName.Mid(nPos);
sResultsIndexFileName.TrimLeft( _T("\\") );
return sResultsIndexFileName;
}
return _T("");
}
bool CProjectLintAnalyser::Analyse(void)
{
ATLASSERT(!m_sProjectFilePathName.IsEmpty() );
bool bResult = false;
if (!m_sProjectFilePathName.IsEmpty() )
{
if (m_arrayImplementationFilePathNames.GetCount() > 0)
{
if (!Utils::FolderExists(m_sResultsFileFolder) )
{
// Create target and intermediate folders as required
(void)::SHCreateDirectoryEx(NULL, m_sResultsFileFolder, NULL);
}
m_nWarningCount = 0;
// Set project specific environment variables
ATLVERIFY(SetEnvironmentVariables() );
bResult = LintFiles(m_arrayImplementationFilePathNames);
// Clear project specific environment variables
ATLVERIFY(ResetEnvironmentVariables() );
}
}
return bResult;
}
bool CProjectLintAnalyser::LintFiles(const CAtlArray<CString>& arrayPathNames)
{
bool bResult = false;
CString sMsg = _T("Analysing project: ") + m_sProjectName + _T("...");
cout << CStringA(sMsg).GetString() << endl << endl;
(void)SetCurrentDirectory(m_sProjectFileFolder);
// Run PC-Lint to generate the .lnt file for the project. This file contains the options to use in the analysis
//
// We use _tsystem() for this as its the simplest way to run a command line process with redirected output.
// e.g. c:\lint\lint-nt.exe <modulename>.dsp >Lint\<modulename>.lnt
CString sLintOptionsFileName;
if (!m_sConfiguration.IsEmpty() )
{
CString sConfiguration = m_sConfiguration;
sConfiguration.Replace( _T("|"), _T("_") );
sConfiguration.Replace( _T(" "), _T("_") );
sLintOptionsFileName.Format( _T("%s_%s.lnt"), m_sProjectName, sConfiguration);
}
else
{
sLintOptionsFileName.Format( _T("%s.lnt"), m_sProjectName);
}
sMsg.Format( _T("Generating project options file %s (Configuration: %s)"),
sLintOptionsFileName,
!m_sConfigurationString.IsEmpty() ? m_sConfigurationString : CString( _T("Unknown") ) );
cout << CStringA(sMsg).GetString() << endl << endl;
CString sParams;
ATLASSERT(!m_sLintFolder.IsEmpty() );
CString sConfigurationOption;
if (!m_sConfigurationString.IsEmpty() )
{
if ( IsVSDotNetProject() )
{
sConfigurationOption.Format( _T("+d\"Configuration=%s\""), m_sConfigurationString);
}
else
{
sConfigurationOption.Format( _T("+d\"CFG=%s\""), m_sConfigurationString);
}
}
sParams.Format( _T("+linebuf %s -i\"%s\" %s \"%s\" >\"%s\""),
m_sAdditionalProjectParams,
m_sLintFolder,
sConfigurationOption,
m_sProjectFilePathName,
sLintOptionsFileName);
// Note that if PC-Lint is installed in a folder with a pathname containing spaces, _tsystem()
// will fail as it doesn't understand long filenames. To work around this, we obtain the short
// pathname of the PC-Lint executable and pass that to the command interpreter instead.
CString sShortPathName;
(void)GetShortPathName(m_sLintFolder + _T("\\lint-nt.exe"), sShortPathName.GetBuffer(_MAX_PATH + 1), _MAX_PATH);
sShortPathName.ReleaseBuffer();
(void)_tsystem(sShortPathName + _T(" ") + sParams);
m_timeStarted = COleDateTime::GetCurrentTime();
// Prescan to add all files, so they show as "pending"
for (size_t n = 0; n < arrayPathNames.GetCount(); n++)
{
const CString sPathName = arrayPathNames[n];
ATLASSERT(!sPathName.IsEmpty() );
if (!sPathName.IsEmpty() )
{
CFileLintAnalyser FileAnalyser( sPathName,
m_sProjectFilePathName,
m_sSolutionFileFolder, // [Alex McCarthy 21.10.2008]
m_sPlatformName, // [Alex McCarthy 23.12.2008]
m_sResultsFileFolder,
m_sLintConfigFile,
m_sAdditionalParams,
m_sConfiguration,
m_sIntermediateFilesFolder);
ATLVERIFY(FileAnalyser.SetLintFolder(m_sLintFolder) );
m_arrayFileResults.Add(FileAnalyser);
}
}
ATLVERIFY(WriteResultsIndexFile() );
if (m_bShowHtml)
{
// Open the output file in a browser window
(void)::ShellExecute( NULL,
_T("open"),
m_sResultsIndexFilePathName,
NULL,
NULL,
SW_SHOWNORMAL);
}
// Now do the analysis
for (size_t n = 0; n < m_arrayFileResults.GetCount(); n++)
{
CFileLintAnalyser& rFileAnalyser = m_arrayFileResults.GetAt(n);
ATLVERIFY(rFileAnalyser.Analyse() );
const int nWarnings = rFileAnalyser.GetWarningCount();
m_nWarningCount += nWarnings;
ATLVERIFY(WriteResultsIndexFile() );
bResult = true;
}
CString sSummary;
sSummary.Format( _T("%d issues for project %s"), m_nWarningCount, m_sProjectName);
cout << endl << CStringA(sSummary).GetString() << endl <<endl;
m_bAnalysed = true;
return bResult;
}
int CProjectLintAnalyser::ReadProjectFile( const CString& sProjectFilePathName,
const CString& sConfiguration,
CAtlArray<CString>& rarrayPathNames,
CString& rsConfigurationString,
CString& rsIntermediateFilesFolder)
{
ATLASSERT(!sProjectFilePathName.IsEmpty() );
CString sErrorMsg;
if (!Utils::FileExists(sProjectFilePathName) )
{
return -1;
}
AddInSolutionModel::CProjectFileReader parser;
const bool bReadOk = parser.Read(sProjectFilePathName);
ATLASSERT(bReadOk);
if (bReadOk)
{
(void)parser.GetConfigurations(m_arrayConfigurations);
rsConfigurationString = parser.GetConfigurationString(sConfiguration);
rsIntermediateFilesFolder = parser.GetIntermediateFilesFolder(sConfiguration);
CAtlArray<CString> arrayCppPathNames, arrayCPathNames;
(void)parser.GetFiles(arrayCppPathNames, _T(".cpp") );
(void)parser.GetFiles(arrayCPathNames, _T(".c") );
rarrayPathNames.RemoveAll();
(void)rarrayPathNames.Append(arrayCppPathNames);
(void)rarrayPathNames.Append(arrayCPathNames);
return static_cast<int>(rarrayPathNames.GetCount() );
}
return -1;
}
bool CProjectLintAnalyser::WriteResultsIndexFile(void)
{
if (m_timeStarted.m_dt > 0.0)
{
COleDateTimeSpan timeElapsed(COleDateTime::GetCurrentTime() - m_timeStarted);
// BR 23/10/2008: Allow for long analysis times without rollover (longer than 59 minutes and 59 seconds)
m_sElapsedTime = timeElapsed.Format( _T("%H hours, %M minutes, %S seconds") );
}
const CString sXslTemplate = Utils::LoadXsl(_Module.GetResourceInstance(), IDR_LINT_PROJECT_TEMPLATE_HTML);
ATLASSERT(!sXslTemplate.IsEmpty() );
Utils::CSplitPath split(m_sResultsIndexFilePathName);
const CString sXmlResultsFilePathName = split.GetDrive() +
split.GetDirectory() +
split.GetFileName() + _T(".xml");
ATLVERIFY(WriteXml(sXmlResultsFilePathName) );
CString sErrorMsg;
const bool bResult = WriteReport(m_sResultsIndexFilePathName, sXslTemplate, &sErrorMsg);
if ( !sErrorMsg.IsEmpty() )
{
cout << CStringA(sErrorMsg).GetString() << endl;
}
if (NULL != m_pSolutionAnalyser)
{
ATLVERIFY(m_pSolutionAnalyser->WriteResultsIndexFile() );
}
(void)PostWriteResultsFile(m_sResultsIndexFilePathName);
return bResult;
}
/// \brief Called when the results HTML file has been updated
///
/// Ideally, when this happens any browser windows with the file open should be refreshed
///
bool CProjectLintAnalyser::PostWriteResultsFile(const CString& sResultsIndexFilePathName)
{
ATLASSERT(Utils::FileExists(sResultsIndexFilePathName) );
if (m_bShowHtml && Utils::FileExists(sResultsIndexFilePathName) )
{
ATLVERIFY(Utils::RefreshAllOpenBrowserWindows(sResultsIndexFilePathName) );
return true;
}
return false;
} //lint !e1961 (Elective Note -- virtual member function could be made const --- Eff. C++ 3rd Ed. item 3)
/// \brief Write an XML Report stream to the given file
///
/// \param sPathName The pathname of the output file
/// \param psMsg A pointer to a CString object to return error information. May be NULL
/// \return true if the file was written sucessfully, false otherwise
///
bool CProjectLintAnalyser::WriteXml(const CString& sPathName, CString* psMsg /*= NULL*/)
{
ATLASSERT(!sPathName.IsEmpty() );
bool bResult = false;
CString sErrorMsg;
if (!sPathName.IsEmpty() )
{
try
{
MSXML2::IXMLDOMDocumentPtr ptrDOMDoc;
// Create empty DOM
HRESULT hr = ptrDOMDoc.CreateInstance("Msxml2.DOMDocument.3.0");
if (FAILED(hr) )
{
sErrorMsg = _T("Failed to create XML DOM");
throw _com_error(hr);
}
// tell our shape to save its state as XML (into ptrDOMDoc)
hr = GenerateXml(ptrDOMDoc, &sErrorMsg);
if (FAILED(hr) )
{
sErrorMsg = _T("Failed to save XML stream - ") + sErrorMsg;
throw _com_error(hr);
}
try
{
const _variant_t varPathName = static_cast<LPCTSTR>(sPathName);
ATLVERIFY(SUCCEEDED(ptrDOMDoc->save(varPathName) ) );
bResult = true;
}
catch (const _com_error& e)
{
sErrorMsg = static_cast<LPCTSTR>(e.Description() );
}
}
catch (const _com_error& e)
{
sErrorMsg = static_cast<LPCTSTR>(e.Description() );
}
}
if (!bResult)
{
if ( (NULL != psMsg) && !sErrorMsg.IsEmpty() )
{
*psMsg = sErrorMsg;
}
ATLTRACE("ERROR in CXmlReportGenerator::WriteXml(): %s\n", sErrorMsg);
}
return bResult;
} //lint !e1762
/// \brief Generate a Report and write it to the given file
///
/// \param sPathName The pathname of the output file
/// \param sXslStyleSheetText The text of the XSLT stylesheet
/// \param psMsg A pointer to a CString object to return error information. May be NULL
/// \return true if the file was written sucessfully, false otherwise
///
bool CProjectLintAnalyser::WriteReport( const CString& sPathName,
const CString& sXslStyleSheetText,
CString* psMsg /*= NULL*/)
{
ATLASSERT(!sPathName.IsEmpty() );
bool bResult = false;
CString sErrorMsg;
if (!sPathName.IsEmpty() )
{
const CString sReportText = GenerateReportText( sXslStyleSheetText,
&sErrorMsg);
if (!sReportText.IsEmpty() )
{
bResult = SUCCEEDED(Utils::WriteTextFile(sPathName, sReportText) );
}
}
if (!bResult)
{
if ( (NULL != psMsg) && !sErrorMsg.IsEmpty() )
{
*psMsg = sErrorMsg;
}
ATLTRACE("ERROR in CProjectLintAnalyser::WriteReport(): %s\n", sErrorMsg);
}
return bResult;
} //lint !e1762
CString CProjectLintAnalyser::GenerateReportText( const CString& sXslStyleSheetText,
CString* psMsg /*= NULL*/) const
{
CString sReportText;
ATLASSERT(!sXslStyleSheetText.IsEmpty() );
if (!sXslStyleSheetText.IsEmpty() )
{
CString sErrorMsg;
try
{
MSXML2::IXMLDOMDocumentPtr ptrDOMDoc;
MSXML2::IXMLDOMDocumentPtr ptrXSLTDOMDoc;
// Create empty DOM
HRESULT hr = ptrDOMDoc.CreateInstance(L"Msxml2.DOMDocument.3.0");
if (FAILED(hr) )
{
sErrorMsg = _T("Failed to create XML DOM");
throw _com_error(hr);
}
hr = ptrXSLTDOMDoc.CreateInstance(L"Msxml2.DOMDocument.3.0");
if (FAILED(hr) )
{
sErrorMsg = _T("Failed to create XSLT DOM");
throw _com_error(hr);
}
ptrXSLTDOMDoc->async = false;
ATLVERIFY(ptrXSLTDOMDoc->loadXML( _bstr_t(sXslStyleSheetText) ) );
sReportText = GenerateReportText( ptrDOMDoc,
ptrXSLTDOMDoc,
psMsg);
// Workaround for XSLT bug: replace <cr><cr><lf> sequences with <cr><lf>
sReportText.Replace( _T("\r\n"), _T("\n") );
}
catch (const _com_error& e)
{
const HRESULT hr = e.Error();
ATLTRACE( _T("Unable to display report. Error code 0x%08x"), hr);
if (NULL != psMsg)
{
psMsg->Format( _T("CProjectLintAnalyser::GenerateReportText()\nException 0x%08x"), hr);
}
}
}
return sReportText;
}
/// \brief Generate a report using the given XML DOM document and XSLT stylesheet
///
/// \param ptrDOMDoc An interface pointer to the XML DOM document containing the stream
/// \param pXslStyleSheetDoc An interface pointer to an XML DOM document containing the XSLT template
/// \param psMsg A pointer to a CString object to return error information. May be NULL
/// \return A Cstring object containing the text of the report
///
CString CProjectLintAnalyser::GenerateReportText( const MSXML2::IXMLDOMDocumentPtr& ptrXmlDoc,
const MSXML2::IXMLDOMDocumentPtr& ptrXslStyleSheetDoc,
CString* psMsg /*= NULL*/) const
{
USES_CONVERSION;
CString sErrorMsg;
CString sReportText;
try
{
// Generate the XML in ptrDOMDoc
const HRESULT hr = GenerateXml(ptrXmlDoc, &sErrorMsg);
if (FAILED(hr) )
{
sErrorMsg = _T("Failed to generate XML stream - ") + sErrorMsg;
throw;
}
ptrXslStyleSheetDoc->async = false;
// Perform the transformation
sReportText = OLE2CT(ptrXmlDoc->transformNode(ptrXslStyleSheetDoc) );
}
catch (const _com_error& e)
{
const HRESULT hr = e.Error();
ATLTRACE( _T("Unable to display report. Error code 0x%08x"), hr);
sErrorMsg.Format( _T("CSolutionLintAnalyser::GenerateReportText()\nException 0x%08x"), hr);
}
if (sReportText.IsEmpty())
{
if ( (NULL != psMsg) && !sErrorMsg.IsEmpty() )
{
*psMsg = sErrorMsg;
}
ATLTRACE( _T("ERROR in CProjectLintAnalyser::GenerateReportText(): %s\n"), sErrorMsg);
}
return sReportText;
}
/// \brief Generate the XML stream
///
/// \param pDOMDoc An interface pointer to the XML DOM document containing the stream
/// \param psMsg A pointer to a CString object to return error information. May be NULL
/// \return A Cstring object containing the text of the report
///
HRESULT CProjectLintAnalyser::GenerateXml( const MSXML2::IXMLDOMDocumentPtr& ptrDOMDoc,
CString* psMsg /*= NULL*/) const
{
USES_CONVERSION;
HRESULT hr = S_OK;
try
{
// Add xml version node
const CString sAttribs = _T("version=\"1.0\" encoding=\"iso-8859-1\"");
MSXML2::IXMLDOMProcessingInstructionPtr ptrIns;
ptrIns = ptrDOMDoc->createProcessingInstruction(L"xml", T2COLE(sAttribs) );
(void)ptrDOMDoc->appendChild(ptrIns);
// Create root element
MSXML2::IXMLDOMElementPtr const ptrRoot = ptrDOMDoc->createElement(L"ProjectLintResults");
(void)ptrDOMDoc->appendChild(ptrRoot);
Utils::CModuleVersion ver;
ATLVERIFY(ver.GetFileVersionInfo() );
const CString sVersion = ver.GetValue( _T("FileVersion") );
MSXML2::IXMLDOMElementPtr const ptrApplication = ptrDOMDoc->createElement(L"Application");
ATLVERIFY(SUCCEEDED(ptrApplication->setAttribute(L"Name", PRODUCT_NAME) ) );
ATLVERIFY(SUCCEEDED(ptrApplication->setAttribute(L"Version", T2COLE(sVersion) ) ) );
ATLVERIFY(SUCCEEDED(ptrApplication->setAttribute(L"Url", PRODUCT_URL) ) );
(void)ptrRoot->appendChild(ptrApplication);
ATLVERIFY(SUCCEEDED(GenerateXml(ptrDOMDoc,
ptrRoot,
psMsg) ) );
}
catch (const _com_error& e)
{
hr = e.Error();
if (NULL != psMsg)
{
psMsg->Format( _T("CProjectLintAnalyser::GenerateXml()\nException 0x%08x"), hr);
}
}
return hr;
}
HRESULT CProjectLintAnalyser::GenerateXml( const MSXML2::IXMLDOMDocumentPtr& ptrDOMDoc,
const MSXML2::IXMLDOMElementPtr& ptrParentElement,
CString* psMsg /*= NULL*/) const
{
USES_CONVERSION;
HRESULT hr = S_OK;
try
{
MSXML2::IXMLDOMElementPtr const ptrProject = ptrDOMDoc->createElement(L"Project");
MSXML2::IXMLDOMAttributePtr const ptrProjectNameAttr = ptrDOMDoc->createAttribute(L"name");
ptrProjectNameAttr->value = _bstr_t(m_sProjectName);
(void)ptrProject->setAttributeNode(ptrProjectNameAttr);
if (!m_sSolutionFileResultFolder.IsEmpty() && (m_timeStarted.m_dt > 0.0) )
{
MSXML2::IXMLDOMAttributePtr const ptrResultsFileAttr = ptrDOMDoc->createAttribute( L"resultsfile");
CString sResultsIndexFile = Utils::CreateRelativePath(m_sSolutionFileResultFolder, m_sResultsIndexFilePathName);
// Replace backslashes in paths with forward slashes. This seems to circumvent problems with
// the Windows XP Local Machine Lockdown policy.
sResultsIndexFile.Replace( _T("\\"), _T("/") );
ptrResultsFileAttr->value = _bstr_t(sResultsIndexFile);
(void)ptrProject->setAttributeNode(ptrResultsFileAttr);
}
MSXML2::IXMLDOMAttributePtr const ptrConfigurationAttr = ptrDOMDoc->createAttribute(L"configuration");
ptrConfigurationAttr->value = _bstr_t(m_sConfiguration);
(void)ptrProject->setAttributeNode(ptrConfigurationAttr);
(void)ptrParentElement->appendChild(ptrProject);
MSXML2::IXMLDOMElementPtr const ptrFiles = ptrDOMDoc->createElement(L"Files");
(void)ptrProject->appendChild(ptrFiles);
for (size_t n = 0; n < m_arrayFileResults.GetCount(); n++)
{
const CFileLintAnalyser& file = m_arrayFileResults[n];
ATLVERIFY(SUCCEEDED(file.GenerateXml( ptrDOMDoc,
ptrFiles,
psMsg) ) );
}
MSXML2::IXMLDOMElementPtr const ptrStatus = ptrDOMDoc->createElement(L"Status");
ptrStatus->text = m_bAnalysed ? L"Complete" : L"Pending";
(void)ptrProject->appendChild(ptrStatus);
MSXML2::IXMLDOMElementPtr const ptrSummary = ptrDOMDoc->createElement(L"Summary");
(void)ptrProject->appendChild(ptrSummary);
MSXML2::IXMLDOMElementPtr const ptrWarnings = ptrDOMDoc->createElement(L"Warnings");
ptrWarnings->text = T2COLE(Utils::IntToStr(m_nWarningCount) );
(void)ptrSummary->appendChild(ptrWarnings);
MSXML2::IXMLDOMElementPtr const ptrStartTime = ptrDOMDoc->createElement(L"StartTime");
const CString sStartTime = m_timeStarted.Format( _T("%a %d %b %Y, %H:%M:%S") );
ptrStartTime->text = T2COLE(sStartTime);
(void)ptrSummary->appendChild(ptrStartTime);
MSXML2::IXMLDOMElementPtr const ptrElapsedTime = ptrDOMDoc->createElement(L"ElapsedTime");
ptrElapsedTime->text = _bstr_t(m_sElapsedTime);
(void)ptrSummary->appendChild(ptrElapsedTime);
}
catch (const _com_error& e)
{
hr = e.Error();
if (NULL != psMsg)
{
psMsg->Format( _T("Exception 0x%08x in CProjectLintAnalyser::GenerateXml()\n"), hr);
}
}
return hr;
}
/// \brief Set solution specific environment variables.
///
/// \return \em true if everything worked; \em false otherwise.
///
bool CProjectLintAnalyser::SetEnvironmentVariables(void)
{
ATLVERIFY(::SetEnvironmentVariable( _T("ProjectDir"), m_sProjectFileFolder) );
ATLVERIFY(::SetEnvironmentVariable( _T("InputDir"), m_sProjectFileFolder) );
if (!m_sConfiguration.IsEmpty() )
{
ATLVERIFY(::SetEnvironmentVariable( _T("ConfigurationName"), m_sConfiguration) );
}
// [Alex McCarthy 21.10.2008]
if (!m_sSolutionFileFolder.IsEmpty() )
{
ATLVERIFY(::SetEnvironmentVariable( _T("SolutionDir"), m_sSolutionFileFolder) );
}
// [Alex McCarthy 23.12.2008]
if (!m_sPlatformName.IsEmpty() )
{
ATLVERIFY(::SetEnvironmentVariable( _T("PlatformName"), m_sPlatformName) );
}
// End changes by [Alex McCarthy]
return true;
} //lint !e1762 (Information -- Member function could be made const --- Eff. C++ 3rd Ed. item 3)
/// \brief Reset solution specific environment variables.
///
/// \return \em true if everything worked; \em false otherwise.
///
bool CProjectLintAnalyser::ResetEnvironmentVariables(void)
{
ATLVERIFY(::SetEnvironmentVariable( _T("ProjectDir"), _T("") ) );
ATLVERIFY(::SetEnvironmentVariable( _T("InputDir"), _T("") ) );
ATLVERIFY(::SetEnvironmentVariable( _T("SolutionDir"), _T("") ) );
ATLVERIFY(::SetEnvironmentVariable( _T("PlatformName"), _T("") ) );
return true;
} //lint !e1762 (Information -- Member function could be made const --- Eff. C++ 3rd Ed. item 3)