/************************************************************************
*
* Description : CVc6ProjectFileReaderImpl - implementation class to parse
* Visual C++ project files for Visual C+ 6.0 and eMbedded
* Visual C++ 4.0.
*
* (c) Copyright 2000-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/AddIns/Visual Lint/Development/Libraries/AddInSolutionModel/src/Vc6ProjectFileReaderImpl.h $
* $Revision: 7 $
* $Date: 14/04/08 11:31 $
* $Author: Anna $
*
* $Nokeywords: $
************************************************************************/
/// \file
/// \brief Implementation class for CProjectFileReader.
#pragma once
#include "ProjectFileReaderImpl.h"
#define PLACEHOLDER_CONFIG _T("VL_placeholder_config")
namespace Riverblade
{
namespace Libraries
{
namespace AddInSolutionModel
{
/// \brief Implementation class to read Visual C++ 6.0 and eMbedded Visual C++ 4.0 project files.
///
class CVc6ProjectFileReaderImpl : public CProjectFileReaderImpl
{
public:
/// \brief Default constructor.
///
CVc6ProjectFileReaderImpl(void)
: CProjectFileReaderImpl(),
m_bParsingFile(false),
m_sLastConfiguration(_T("") )
{
}
/// \brief Class destructor
///
virtual ~CVc6ProjectFileReaderImpl(void)
{
}
// Data members
private:
bool m_bParsingFile; ///< Implementation var set to indicate that we are currently parsing a file definition.
CString m_sLastConfiguration; ///< Implementation var holding the name of the last configuration we parsed.
// Virtual overrides
public:
/// \brief Parse the given Visual C++ 6.0 project file contents.
///
/// \param sFileText The text of the project file.
/// \return \em true if the text was parsed successfully;
/// \em false otherwise.
///
virtual bool Parse(const CString& sFileText)
{
CAtlArray<CString> arrayLines;
(void)Utils::StrToArray(sFileText, arrayLines);
m_sLastConfiguration = PLACEHOLDER_CONFIG;
if (arrayLines.GetCount() > 0)
{
// The file definitely exists and has been read successfully
// From here on in its up to the parser...
for (size_t nLineNo = 0; nLineNo < arrayLines.GetCount(); nLineNo++)
{
const CString& sLine = arrayLines[nLineNo];
(void)ParseLine(sLine);
}
return true;
}
return false;
}
/// \brief Return the "configuration string" for a given configuration within the project.
///
/// \param sConfig The name of the configuration for which the configuration
/// string should be returned (e.g "Win32 Debug").
/// \return The corresponding configuration string, or an empty string
/// if the configuration name is not recognised.
///
virtual CString GetConfigurationString(const CString& sConfig) const
{
CString sUnused;
if (m_mapAvailableConfigurations.Lookup(sConfig, sUnused) )
{
CString sConfigurationString;
sConfigurationString.Format(_T("%s - %s"), m_sProjectName, sConfig);
return sConfigurationString;
}
return _T("");
}
// Implementation
public: // For unit testing only
/// \brief Implementation method to parse the given line
///
/// \param sLine The text line to parse
/// \return \em true if parsed successfully; \em false otherwise.
///
bool ParseLine(const CString& sLine)
{
if (!sLine.IsEmpty())
{
if (sLine.Find( _T(" Name=") ) > 1)
{
// e.g. "# Microsoft Developer Studio Project File - Name="ResOrgCore" - Package Owner=<4>"
m_sProjectName = Utils::Before(Utils::After(sLine, _T("Name=") ), _T("-") );
(void)m_sProjectName.TrimLeft();
(void)m_sProjectName.TrimRight();
(void)m_sProjectName.TrimLeft( _T("\"") );
(void)m_sProjectName.TrimRight( _T("\"") );
}
else if (sLine.Find( _T("\"$(CFG)\"") ) > 0)
{
// Store details of the configurations we find in this project file
// The configurations are identified by lines such as:
//
// !IF "$(CFG)" == "ResOrgCore - Win32 Release"
//
// in the .dsp file.
CString sConfigurationString = Utils::After(sLine, _T("==") );
(void)sConfigurationString.TrimLeft();
(void)sConfigurationString.TrimLeft( _T("\"") );
(void)sConfigurationString.TrimRight( _T("\"") );
CString sConfiguration = Utils::After(sConfigurationString, _T("-") );
(void)sConfiguration.TrimLeft();
CString sUnused;
if (!m_mapAvailableConfigurations.Lookup(sConfiguration, sUnused) )
{
(void)m_mapAvailableConfigurations.SetAt(sConfiguration, _T("") );
}
m_sLastConfiguration = sConfiguration;
}
else if (sLine.Find( _T("# Name") ) >= 0)
{
// e.g. # Name "Quant_BE - Win32 Unicode Debug"
if (PLACEHOLDER_CONFIG == m_sLastConfiguration)
{
// These directives come at the end of the settings, just before the file list. We only
// need to use them if we have not already seen a $(CFG) directive - e.g. if the project
// has only a single configuration [Anna 9.2.2008].
CString sConfiguration = Utils::After(sLine, _T("-") );
sConfiguration.TrimRight(_T("\"") );
sConfiguration.Trim();
// Move the configuration settings from the placeholder configuration
CProjectConfiguration info;
ATLVERIFY(m_mapConfigToSettings.Lookup(m_sLastConfiguration, info) );
info.m_sName = sConfiguration;
(void)m_mapConfigToSettings.SetAt(sConfiguration, info);
(void)m_mapAvailableConfigurations.SetAt(sConfiguration, info.m_sIntermediateFolder);
ATLVERIFY(m_mapConfigToSettings.RemoveKey(m_sLastConfiguration) );
ATLVERIFY(m_mapAvailableConfigurations.RemoveKey(m_sLastConfiguration) );
m_sLastConfiguration = sConfiguration; // Just in case
}
}
else if (sLine.Find( _T("Intermediate_Dir") ) > 0)
{
CString sIntermediateFolder = Utils::After(sLine, _T("Intermediate_Dir") );
(void)sIntermediateFolder.TrimLeft();
(void)sIntermediateFolder.TrimLeft( _T("\"") );
(void)sIntermediateFolder.TrimRight( _T("\"") );
(void)m_mapAvailableConfigurations.SetAt(m_sLastConfiguration, sIntermediateFolder);
}
else if (!m_bParsingFile && (sLine.Find( _T("# ADD CPP") ) >= 0) )
{
// e.g. # ADD CPP /nologo /MDd /W3 /GR /GX /Z7 /Od /Gy /I "$(ES_INCLUDE)" /I "$(ESBASEDIR)/h" /I "$(ESBASEDIR)/../exp/idl/gen" /I "$(ESBASEDIR)/gen/idl/generated" /I "$(ESBASEDIR)/../exp/inc" /I "$(ESBASEDIR)\..\exp_oem\exp\idl\gen" /I "$(ESBASEDIR)/is" /I "$(ESBASEDIR)/pm" /I "$(ESBASEDIR)/../interf/exph" /I "$(ESBASEDIR)/base/stg" /I "$(ESBASEDIR)/base/AdoHelp" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "WIN32" /D "PMDLL_IMPLEMENTATION" /D "AUTO_INCLUDE_SMARTS" /FR /Yu"PmDllAfx.h" /c
// Parse /I and /D directives
// Expand env vars in each
// Concatenate each into a composite path
// Set CProjectConfiguration::m_sAdditionalIncludeDirectories
CProjectConfiguration info;
ATLVERIFY(ParseCppConfiguration(sLine, info.m_sAdditionalIncludeDirectories, info.m_sPreprocessorDefinitions) );
ATLVERIFY(m_mapAvailableConfigurations.Lookup(m_sLastConfiguration, info.m_sIntermediateFolder) );
if (0 == info.m_sIntermediateFolder.Find(_T(".\\") ) )
{
// Remove ".\" from the start of any pathnames, as they can confuse PC-Lint [Anna 12.2.2008].
info.m_sIntermediateFolder = Utils::After(info.m_sIntermediateFolder, _T(".\\") );
}
// info.m_eConfigurationType = ::_tstoi(GetAttribute(sConfigTag, _T("ConfigurationType") ) );
// info.m_eCharacterSet = ::_tstoi(GetAttribute(sConfigTag, _T("CharacterSet") ) );
// CString sMinimiseRTL = GetAttribute(sConfigTag, _T("ATLMinimizesCRunTimeLibraryUsage") );
// info.m_bMinimiseRTL = ( (0 == sMinimiseRTL.CompareNoCase( _T("true") ) ) || (_T("1") == sMinimiseRTL) );
// info.m_eUseOfMfc = ::_tstoi(GetAttribute(sConfigTag, _T("UseOfMFC") ) );
// info.m_eUseOfAtl = ::_tstoi(GetAttribute(sConfigTag, _T("UseOfAtl") ) );
// info.m_eRuntimeLibrary = ::_tstoi(GetAttribute(sCompilerElement, _T("RuntimeLibrary") ) );
info.m_bForceConformanceInForLoopScope = false;
m_mapConfigToSettings[m_sLastConfiguration] = info;
}
else if (sLine.Find( _T("# Begin Source File") ) == 0)
{
m_bParsingFile = true;
}
else if (sLine.Find( _T("# End Source File") ) == 0)
{
m_bParsingFile = false;
}
else if (m_bParsingFile)
{
if (sLine.Find( _T("SOURCE") ) == 0)
{
CString sFile = Utils::After( sLine, _T("=") );
// Strip quotes
(void)sFile.TrimLeft( _T("\"") );
(void)sFile.TrimRight( _T("\"") );
if (!sFile.IsEmpty() )
{
ATLVERIFY(ExpandEnvironmentVars(sFile) );
const CString sPathName = GetPathName(sFile);
if (!sPathName.IsEmpty())
{
(void)m_arrayFiles.Add(sPathName);
}
}
}
if (sLine.Find( _T("# ADD CPP") ) >= 0)
{
//# ADD CPP /X /I ".\wibble" /U "_CONSOLE"
// U = undefine; /X = ignore standard include paths
// /u=$(NOINHERIT)
CFileConfiguration fileconfig;
ATLVERIFY(ParseCppConfiguration(sLine, fileconfig.m_sAdditionalIncludeDirectories, fileconfig.m_sPreprocessorDefinitions) );
ATLASSERT(!m_sLastConfiguration.IsEmpty() );
CProjectConfiguration& rconfig = m_mapConfigToSettings[m_sLastConfiguration];
ATLASSERT(m_arrayFiles.GetCount() >= 1);
const CString sPathName = m_arrayFiles[m_arrayFiles.GetCount() - 1];
ATLVERIFY(rconfig.SetFileConfiguration(sPathName, fileconfig) );
}
}
return true;
}
return false;
}
/// \brief Implementation method to parse "# ADD CPP" directives
///
/// The following actions are performed on "# ADD CPP" directives:
///
/// - Parse /I and /D directives
/// - Expand any environment variables within each
/// - Concatenate each into a composite path
/// - Set CProjectConfiguration::m_sAdditionalIncludeDirectories
///
/// e.g. # ADD CPP /nologo /MDd /W3 /GR /GX /Z7 /Od /Gy /I "$(ES_INCLUDE)" /I "$(ESBASEDIR)/h" /I "$(ESBASEDIR)/../exp/idl/gen" /I "$(ESBASEDIR)/gen/idl/generated" /I "$(ESBASEDIR)/../exp/inc" /I "$(ESBASEDIR)\..\exp_oem\exp\idl\gen" /I "$(ESBASEDIR)/is" /I "$(ESBASEDIR)/pm" /I "$(ESBASEDIR)/../interf/exph" /I "$(ESBASEDIR)/base/stg" /I "$(ESBASEDIR)/base/AdoHelp" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "WIN32" /D "PMDLL_IMPLEMENTATION" /D "AUTO_INCLUDE_SMARTS" /FR /Yu"PmDllAfx.h" /c
///
/// The following options are handled:
///
/// - /I defines include folders
/// - /D defines preprocessor symbols
/// - /MD defines _MT and _DLL
/// - /MDd Defines _DEBUG, _MT, and _DLL
/// - /MLd Defines _DEBUG
/// - /MT Defines _MT
/// - /MTd Defines _DEBUG and _MT.
///
/// \param sLine The source line to parse.
/// \param rsAdditionalIncludeFolders A reference to a string which will hold any additional
/// include folder definitions within the line on exit.
/// \param rsPreprocessorDefinitions A reference to a string which will hold any preprocessor
/// directives within the line on exit.
/// \return \em true if the line was processed correctly; \em false otherwise.
///
bool ParseCppConfiguration( const CString& sLine,
CString& rsAdditionalIncludeFolders,
CString& rsPreprocessorDefinitions) const
{
CString sTemp = Utils::After(sLine, _T("CPP") );
while (!sTemp.IsEmpty() )
{
CString sNextDirective = Utils::Before(sTemp.Mid(1), _T(" /") );
sTemp = Utils::After(sTemp, sNextDirective);
sNextDirective.TrimRight();
const CString sDirective = Utils::Before(sNextDirective, _T(" ") );
sNextDirective.Trim(_T("\"") );
CString sValue = Utils::After(sNextDirective, sDirective);;
sValue.Trim();
sValue.Trim(_T("\"") );
if (0 == sDirective.CompareNoCase( _T("/I") ) )
{
rsAdditionalIncludeFolders += sValue + _T(";");
}
else if (0 == sDirective.CompareNoCase( _T("/D") ) )
{
AddDefinitionIfNotPresent(rsPreprocessorDefinitions, sValue);
}
else if (0 == sDirective.CompareNoCase( _T("/MD") ) )
{
// /MD defines _MT and _DLL
AddDefinitionIfNotPresent(rsPreprocessorDefinitions, _T("_MT") );
AddDefinitionIfNotPresent(rsPreprocessorDefinitions, _T("_DLL") );
}
else if (0 == sDirective.CompareNoCase( _T("/MDd") ) )
{
// /MDd Defines _DEBUG, _MT, and _DLL
AddDefinitionIfNotPresent(rsPreprocessorDefinitions, _T("_DEBUG") );
AddDefinitionIfNotPresent(rsPreprocessorDefinitions, _T("_MT") );
AddDefinitionIfNotPresent(rsPreprocessorDefinitions, _T("_DLL") );
}
else if (0 == sDirective.CompareNoCase( _T("/MLd") ) )
{
// /MLd Defines _DEBUG
AddDefinitionIfNotPresent(rsPreprocessorDefinitions, _T("_DEBUG") );
}
else if (0 == sDirective.CompareNoCase( _T("/MT") ) )
{
// /MT Defines _MT
AddDefinitionIfNotPresent(rsPreprocessorDefinitions, _T("_MT") );
}
else if (0 == sDirective.CompareNoCase( _T("/MTd") ) )
{
// /MTd Defines _DEBUG and _MT
AddDefinitionIfNotPresent(rsPreprocessorDefinitions, _T("_DEBUG") );
AddDefinitionIfNotPresent(rsPreprocessorDefinitions, _T("_MT") );
}
else if (0 == sDirective.CompareNoCase( _T("/GR") ) )
{
// /GR Defines _CPPRTTI
AddDefinitionIfNotPresent(rsPreprocessorDefinitions, _T("_CPPRTTI") );
}
else if (_T("/u") == sDirective) // Case sensitive
{
// /u undefines all project symbols - mark its presence using $(NOINHERIT)
AddDefinitionIfNotPresent(rsPreprocessorDefinitions, NOINHERIT);
}
}
ATLVERIFY(ExpandEnvironmentVars(rsAdditionalIncludeFolders) );
rsAdditionalIncludeFolders.TrimRight( _T(";") );
rsPreprocessorDefinitions.TrimRight( _T(";") );
return true;
}
/// \brief Implementation method to add the given definition to the given string if not already present.
///
/// \param rsPreprocessorDefinitions A reference to a string which will receive the preprocessor directives.
/// \param sDefinitionToAdd A string containing the preprocessor directive to add.
///
static void AddDefinitionIfNotPresent(CString& rsPreprocessorDefinitions, const CString& sDefinitionToAdd)
{
if (rsPreprocessorDefinitions.Find(sDefinitionToAdd + _T(";") ) < 0)
{
rsPreprocessorDefinitions += sDefinitionToAdd + _T(";");
}
}
/* TODO: Handle per-file configurations
e.g.:
# Begin Source File
SOURCE=.\configtest.cpp
!IF "$(CFG)" == "configtest - Win32 Release"
!ELSEIF "$(CFG)" == "configtest - Win32 Debug"
# ADD CPP /I "..\\" /I ".\Include" /D "FOOBLE" /D "_BOOBLE" /D "_BLETCH"
!ENDIF
*/
};
}; // namespace AddInSolutionModel
}; // namespace Libraries
}; //namespace Riverblade