Commit df035e48 authored by Petr Kmoch's avatar Petr Kmoch Committed by Brad King
Browse files

VS: Create parser for Visual Studio .sln files

Create class cmVisualStudioSlnParser as a generic parser for Visual
Studio .sln files. Implement minimum functionality but keep class
extensible.  Add tests for the class.
parent de8be9ef
......@@ -357,6 +357,10 @@ if (WIN32)
cmLocalVisualStudio7Generator.h
cmLocalVisualStudioGenerator.cxx
cmLocalVisualStudioGenerator.h
cmVisualStudioSlnData.h
cmVisualStudioSlnData.cxx
cmVisualStudioSlnParser.h
cmVisualStudioSlnParser.cxx
cmVisualStudioWCEPlatformParser.h
cmVisualStudioWCEPlatformParser.cxx
cmWin32ProcessExecution.cxx
......
/*============================================================================
CMake - Cross Platform Makefile Generator
Copyright 2000-2013 Kitware, Inc., Insight Software Consortium
Distributed under the OSI-approved BSD License (the "License");
see accompanying file Copyright.txt for details.
This software is distributed WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the License for more information.
============================================================================*/
#include "cmVisualStudioSlnData.h"
//----------------------------------------------------------------------------
const cmSlnProjectEntry*
cmSlnData::GetProjectByGUID(const std::string& projectGUID) const
{
ProjectStorage::const_iterator it(ProjectsByGUID.find(projectGUID));
if (it != ProjectsByGUID.end())
return &it->second;
else
return NULL;
}
//----------------------------------------------------------------------------
const cmSlnProjectEntry*
cmSlnData::GetProjectByName(const std::string& projectName) const
{
ProjectStringIndex::const_iterator it(ProjectNameIndex.find(projectName));
if (it != ProjectNameIndex.end())
return &it->second->second;
else
return NULL;
}
//----------------------------------------------------------------------------
std::vector<cmSlnProjectEntry> cmSlnData::GetProjects() const
{
ProjectStringIndex::const_iterator it(this->ProjectNameIndex.begin()),
itEnd(this->ProjectNameIndex.end());
std::vector<cmSlnProjectEntry> result;
for (; it != itEnd; ++it)
result.push_back(it->second->second);
return result;
}
//----------------------------------------------------------------------------
cmSlnProjectEntry* cmSlnData::AddProject(
const std::string& projectGUID,
const std::string& projectName,
const std::string& projectRelativePath)
{
ProjectStorage::iterator it(ProjectsByGUID.find(projectGUID));
if (it != ProjectsByGUID.end())
return NULL;
it = ProjectsByGUID.insert(
ProjectStorage::value_type(
projectGUID,
cmSlnProjectEntry(projectGUID, projectName, projectRelativePath))).first;
ProjectNameIndex[projectName] = it;
return &it->second;
}
/*============================================================================
CMake - Cross Platform Makefile Generator
Copyright 2000-2013 Kitware, Inc., Insight Software Consortium
Distributed under the OSI-approved BSD License (the "License");
see accompanying file Copyright.txt for details.
This software is distributed WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the License for more information.
============================================================================*/
#ifndef cmVisualStudioSlnData_h
#define cmVisualStudioSlnData_h
#include "cmStandardIncludes.h"
class cmSlnProjectEntry
{
public:
cmSlnProjectEntry() {}
cmSlnProjectEntry(const std::string& guid,
const std::string& name,
const std::string& relativePath)
: Guid(guid), Name(name), RelativePath(relativePath)
{}
std::string GetGUID() const { return Guid; }
std::string GetName() const { return Name; }
std::string GetRelativePath() const { return RelativePath; }
private:
std::string Guid, Name, RelativePath;
};
class cmSlnData
{
public:
const cmSlnProjectEntry*
GetProjectByGUID(const std::string& projectGUID) const;
const cmSlnProjectEntry*
GetProjectByName(const std::string& projectName) const;
std::vector<cmSlnProjectEntry> GetProjects() const;
cmSlnProjectEntry* AddProject(const std::string& projectGUID,
const std::string& projectName,
const std::string& projectRelativePath);
private:
typedef std::map<std::string, cmSlnProjectEntry> ProjectStorage;
ProjectStorage ProjectsByGUID;
typedef std::map<std::string, ProjectStorage::iterator> ProjectStringIndex;
ProjectStringIndex ProjectNameIndex;
};
#endif
This diff is collapsed.
/*============================================================================
CMake - Cross Platform Makefile Generator
Copyright 2000-2013 Kitware, Inc., Insight Software Consortium
Distributed under the OSI-approved BSD License (the "License");
see accompanying file Copyright.txt for details.
This software is distributed WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the License for more information.
============================================================================*/
#ifndef cmVisualStudioSlnParser_h
#define cmVisualStudioSlnParser_h
#include "cmStandardIncludes.h"
#include <bitset>
class cmSlnData;
class cmVisualStudioSlnParser
{
public:
enum ParseResult
{
ResultOK = 0,
ResultInternalError = -1,
ResultExternalError = 1,
ResultErrorOpeningInput = ResultExternalError,
ResultErrorReadingInput,
ResultErrorInputStructure,
ResultErrorInputData,
ResultErrorBadInternalState = ResultInternalError,
ResultErrorUnsupportedDataGroup = ResultInternalError - 1
};
enum DataGroup
{
DataGroupProjectsBit,
DataGroupProjectDependenciesBit,
DataGroupSolutionConfigurationsBit,
DataGroupProjectConfigurationsBit,
DataGroupSolutionFiltersBit,
DataGroupGenericGlobalSectionsBit,
DataGroupCount
};
typedef std::bitset<DataGroupCount> DataGroupSet;
static const DataGroupSet DataGroupProjects;
static const DataGroupSet DataGroupProjectDependencies;
static const DataGroupSet DataGroupSolutionConfigurations;
static const DataGroupSet DataGroupProjectConfigurations;
static const DataGroupSet DataGroupSolutionFilters;
static const DataGroupSet DataGroupGenericGlobalSections;
static const DataGroupSet DataGroupAll;
bool Parse(std::istream& input,
cmSlnData& output,
DataGroupSet dataGroups = DataGroupAll);
bool ParseFile(const std::string& file,
cmSlnData& output,
DataGroupSet dataGroups = DataGroupAll);
ParseResult GetParseResult() const;
size_t GetParseResultLine() const;
bool GetParseHadBOM() const;
protected:
class State;
friend class State;
class ParsedLine;
struct ResultData
{
ParseResult Result;
size_t ResultLine;
bool HadBOM;
ResultData();
void Clear();
void SetError(ParseResult error, size_t line);
} LastResult;
bool IsDataGroupSetSupported(DataGroupSet dataGroups) const;
bool ParseImpl(std::istream& input, cmSlnData& output, State& state);
bool ParseBOM(std::istream& input, std::string& line, State& state);
bool ParseMultiValueTag(const std::string& line,
ParsedLine& parsedLine,
State& state);
bool ParseSingleValueTag(const std::string& line,
ParsedLine& parsedLine,
State& state);
bool ParseKeyValuePair(const std::string& line,
ParsedLine& parsedLine,
State& state);
bool ParseTag(const std::string& fullTag,
ParsedLine& parsedLine,
State& state);
bool ParseValue(const std::string& value, ParsedLine& parsedLine);
};
#endif
......@@ -12,6 +12,14 @@ set(CMakeLib_TESTS
testXMLSafe
)
if(WIN32 AND NOT UNIX) # Just if(WIN32) when CMake >= 2.8.4 is required
list(APPEND CMakeLib_TESTS
testVisualStudioSlnParser
)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/testVisualStudioSlnParser.h.in
${CMAKE_CURRENT_BINARY_DIR}/testVisualStudioSlnParser.h @ONLY)
endif()
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/testXMLParser.h.in
${CMAKE_CURRENT_BINARY_DIR}/testXMLParser.h @ONLY)
......
#include "testVisualStudioSlnParser.h"
#include "cmVisualStudioSlnData.h"
#include "cmVisualStudioSlnParser.h"
#include <cmsys/ios/iostream>
//----------------------------------------------------------------------------
static bool parsedRight(cmVisualStudioSlnParser& parser,
const std::string& file, cmSlnData& data,
cmVisualStudioSlnParser::ParseResult expected =
cmVisualStudioSlnParser::ResultOK)
{
if (parser.ParseFile(SOURCE_DIR "/testVisualStudioSlnParser_data/" + file
+ "." SLN_EXTENSION,
data, cmVisualStudioSlnParser::DataGroupProjects))
{
if (expected == cmVisualStudioSlnParser::ResultOK)
{
return true;
}
}
else
{
if (parser.GetParseResult() == expected)
{
return true;
}
}
cmsys_ios::cerr << "cmVisualStudioSlnParser mis-parsed " << file
<< "." SLN_EXTENSION << "; expected result " << expected
<< ", got " << parser.GetParseResult()
<< cmsys_ios::endl;
return false;
}
//----------------------------------------------------------------------------
int testVisualStudioSlnParser(int, char*[])
{
cmVisualStudioSlnParser parser;
// Test clean parser
if (parser.GetParseResult() != cmVisualStudioSlnParser::ResultOK)
{
cmsys_ios::cerr << "cmVisualStudioSlnParser initialisation failed"
<< cmsys_ios::endl;
return 1;
}
// Test parsing valid sln
{
cmSlnData data;
if (!parsedRight(parser, "valid", data))
{
return 1;
}
const std::vector<cmSlnProjectEntry>& projects = data.GetProjects();
const char * const names[] =
{
"3rdParty", "ALL_BUILD", "CMakeLib", "CMakeLibTests",
"CMakePredefinedTargets", "CPackLib", "CTestDashboardTargets", "CTestLib",
"Continuous", "Documentation",
"Experimental", "INSTALL", "KWSys", "LIBCURL", "Nightly",
"NightlyMemoryCheck", "PACKAGE", "RUN_TESTS", "Tests", "Utilities",
"Win9xCompat", "ZERO_CHECK", "cmIML_test", "cmake", "cmbzip2", "cmcldeps",
"cmcompress", "cmcurl", "cmexpat", "cmlibarchive", "cmsys",
"cmsysEncodeExecutable", "cmsysProcessFwd9x", "cmsysTestDynload",
"cmsysTestProcess", "cmsysTestSharedForward", "cmsysTestsC",
"cmsysTestsCxx", "cmsys_c", "cmw9xcom", "cmzlib", "cpack", "ctest",
"documentation", "memcheck_fail", "pseudo_BC", "pseudo_purify",
"pseudo_valgrind", "test_clean", "uninstall"
};
const size_t expectedProjectCount = sizeof(names) / sizeof(*names);
if (projects.size() != expectedProjectCount)
{
cmsys_ios::cerr << "cmVisualStudioSlnParser returned bad number of "
<< "projects (" << projects.size() << " instead of "
<< expectedProjectCount << ')'
<< cmsys_ios::endl;
return 1;
}
for (size_t idx = 0; idx < expectedProjectCount; ++idx)
{
if (projects[idx].GetName() != names[idx])
{
cmsys_ios::cerr << "cmVisualStudioSlnParser returned bad project #"
<< idx << "; expected \"" << names[idx] << "\", got \""
<< projects[idx].GetName() << '"'
<< cmsys_ios::endl;
return 1;
}
}
if (projects[0].GetRelativePath() != "Utilities\\3rdParty")
{
cmsys_ios::cerr << "cmVisualStudioSlnParser returned bad relative path of "
<< "project 3rdParty; expected \"Utilities\\3rdParty\", "
<< "got \"" << projects[0].GetRelativePath() << '"'
<< cmsys_ios::endl;
return 1;
}
if (projects[2].GetGUID() != "{59BCCCCD-3AD1-4491-B8F4-C5793AC007E2}")
{
cmsys_ios::cerr << "cmVisualStudioSlnParser returned bad relative path of "
<< "project CMakeLib; expected "
<< "\"{59BCCCCD-3AD1-4491-B8F4-C5793AC007E2}\", "
<< "got \"" << projects[2].GetGUID() << '"'
<< cmsys_ios::endl;
return 1;
}
}
// Test BOM parsing
{
cmSlnData data;
if (!parsedRight(parser, "bom", data))
{
return 1;
}
if (!parser.GetParseHadBOM())
{
cmsys_ios::cerr << "cmVisualStudioSlnParser didn't find BOM in bom."
<< SLN_EXTENSION
<< cmsys_ios::endl;
return 1;
}
if (!parsedRight(parser, "nobom", data))
{
return 1;
}
if (parser.GetParseHadBOM())
{
cmsys_ios::cerr << "cmVisualStudioSlnParser found BOM in nobom."
<< SLN_EXTENSION
<< cmsys_ios::endl;
return 1;
}
}
// Test invalid sln
{
{
cmSlnData data;
if (!parsedRight(parser, "err-nonexistent", data,
cmVisualStudioSlnParser::ResultErrorOpeningInput))
{
return 1;
}
}
{
cmSlnData data;
if (!parsedRight(parser, "err-empty", data,
cmVisualStudioSlnParser::ResultErrorReadingInput))
{
return 1;
}
}
const char * const files[] =
{
"header", "projectArgs", "topLevel", "projectContents", "projectSection",
"global", "unclosed", "strayQuote", "strayParen", "strayQuote2"
};
for (size_t idx = 0; idx < sizeof(files) / sizeof(files[0]); ++idx)
{
cmSlnData data;
if (!parsedRight(parser, std::string("err-structure-") + files[idx], data,
cmVisualStudioSlnParser::ResultErrorInputStructure))
{
return 1;
}
}
{
cmSlnData data;
if (!parsedRight(parser, "err-data", data,
cmVisualStudioSlnParser::ResultErrorInputData))
{
return 1;
}
}
}
// All is well
return 0;
}
#ifndef testVisualStudioSlnParser_h
#define testVisualStudioSlnParser_h
#define SOURCE_DIR "@CMAKE_CURRENT_SOURCE_DIR@"
#define SLN_EXTENSION "sln-file"
#endif
Microsoft Visual Studio Solution File, Format Version 11.00
# Visual Studio 2010
Microsoft Visual Studio Solution File, Format Version 11.00
# Visual Studio 2010
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Continuous", "Continuous.vcxproj", "{E5071092-DBFB-49E2-AF0F-E8B0FDEF6C89}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Continuous", "Continuous.vcxproj", "{E5071092-DBFB-49E2-AF0F-E8B0FDEF6C89}"
EndProject
Microsoft Visual Studio Solution File, Format Version 11.00
# Visual Studio 2010
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Continuous", "Continuous.vcxproj", "{E5071092-DBFB-49E2-AF0F-E8B0FDEF6C89}"
ProjectSection(ProjectDependencies) = postProject
{90BC31D7-A3E8-4F04-8049-2236C239A044} = {90BC31D7-A3E8-4F04-8049-2236C239A044}
EndProjectSection
EndProject
Global
EndGlobalSection
Microsoft Visual Studio Solution2 File, Format Version 11.00
# Visual Studio 2010
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Continuous", "Continuous.vcxproj", "{E5071092-DBFB-49E2-AF0F-E8B0FDEF6C89}"
EndProject
Microsoft Visual Studio Solution File, Format Version 11.00
# Visual Studio 2010
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Continuous", "Continuous.vcxproj"
EndProject
Microsoft Visual Studio Solution File, Format Version 11.00
# Visual Studio 2010
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Continuous", "Continuous.vcxproj", "{E5071092-DBFB-49E2-AF0F-E8B0FDEF6C89}"
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Continuous2", "Continuous2.vcxproj", "{E5071091-DBFB-49E2-AF0F-E8B0FDEF6C89}"
EndProject
EndProject
Microsoft Visual Studio Solution File, Format Version 11.00
# Visual Studio 2010
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ZERO_CHECK", "ZERO_CHECK.vcxproj", "{90BC31D7-A3E8-4F04-8049-2236C239A044}"
ProjectSection(ProjectDependencies) = postProject
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Continuous", "Continuous.vcxproj", "{E5071092-DBFB-49E2-AF0F-E8B0FDEF6C89}"
ProjectSection(ProjectDependencies) = postProject
{90BC31D7-A3E8-4F04-8049-2236C239A044} = {90BC31D7-A3E8-4F04-8049-2236C239A044}
EndProject
EndProject
Microsoft Visual Studio Solution File, Format Version 11.00
# Visual Studio 2010
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}" = "Continuous", "Continuous.vcxproj", "{E5071092-DBFB-49E2-AF0F-E8B0FDEF6C89}"
EndProject
Microsoft Visual Studio Solution File, Format Version 11.00
# Visual Studio 2010
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Continuous", "Continuous.vcxproj", "{E5071092-DBFB-49E2-AF0F-E8B0FDEF6C89}
EndProject
Microsoft Visual Studio Solution File, Format Version 11.00
# Visual Studio 2010
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}) = "Continuous", "Continuous.vcxproj", "{E5071092-DBFB-49E2-AF0F-E8B0FDEF6C89}"
EndProject
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment