diff options
Diffstat (limited to 'Source')
43 files changed, 1317 insertions, 138 deletions
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index baeca29..1893167 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -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 diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake index 317cc73..f3fdc90 100644 --- a/Source/CMakeVersion.cmake +++ b/Source/CMakeVersion.cmake @@ -1,6 +1,6 @@ # CMake version number components. set(CMake_VERSION_MAJOR 2) set(CMake_VERSION_MINOR 8) -set(CMake_VERSION_PATCH 10) -set(CMake_VERSION_TWEAK 20130426) +set(CMake_VERSION_PATCH 11) +set(CMake_VERSION_TWEAK 20130516) #set(CMake_VERSION_RC 1) diff --git a/Source/CPack/cmCPackGenerator.cxx b/Source/CPack/cmCPackGenerator.cxx index 7cc1522..3c685bd 100644 --- a/Source/CPack/cmCPackGenerator.cxx +++ b/Source/CPack/cmCPackGenerator.cxx @@ -638,7 +638,7 @@ int cmCPackGenerator::InstallProjectViaInstallCMakeProjects( = this->MakefileMap->GetDefinition("CMAKE_MAKE_PROGRAM"); std::string buildCommand = globalGenerator->GenerateBuildCommand(cmakeMakeProgram, - installProjectName.c_str(), 0, + installProjectName.c_str(), 0, 0, globalGenerator->GetPreinstallTargetName(), buildConfig, false, false); cmCPackLogger(cmCPackLog::LOG_DEBUG, diff --git a/Source/CTest/cmCTestBuildCommand.cxx b/Source/CTest/cmCTestBuildCommand.cxx index f4d38ce..1f63185 100644 --- a/Source/CTest/cmCTestBuildCommand.cxx +++ b/Source/CTest/cmCTestBuildCommand.cxx @@ -130,10 +130,11 @@ cmCTestGenericHandler* cmCTestBuildCommand::InitializeHandler() cmakeBuildConfiguration = config; } + std::string dir = this->CTest->GetCTestConfiguration("BuildDirectory"); std::string buildCommand = this->GlobalGenerator-> GenerateBuildCommand(cmakeMakeProgram, - cmakeProjectName, + cmakeProjectName, dir.c_str(), cmakeBuildAdditionalFlags, cmakeBuildTarget, cmakeBuildConfiguration, true, false); cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, diff --git a/Source/CTest/cmCTestConfigureCommand.cxx b/Source/CTest/cmCTestConfigureCommand.cxx index d6d39a9..db33cb6 100644 --- a/Source/CTest/cmCTestConfigureCommand.cxx +++ b/Source/CTest/cmCTestConfigureCommand.cxx @@ -31,32 +31,6 @@ cmCTestGenericHandler* cmCTestConfigureCommand::InitializeHandler() cmSystemTools::ExpandListArgument(this->Values[ctc_OPTIONS], options); } - if ( this->Values[ct_BUILD] ) - { - this->CTest->SetCTestConfiguration("BuildDirectory", - cmSystemTools::CollapseFullPath( - this->Values[ct_BUILD]).c_str()); - } - else - { - this->CTest->SetCTestConfiguration("BuildDirectory", - cmSystemTools::CollapseFullPath( - this->Makefile->GetSafeDefinition("CTEST_BINARY_DIRECTORY")).c_str()); - } - - if ( this->Values[ct_SOURCE] ) - { - this->CTest->SetCTestConfiguration("SourceDirectory", - cmSystemTools::CollapseFullPath( - this->Values[ct_SOURCE]).c_str()); - } - else - { - this->CTest->SetCTestConfiguration("SourceDirectory", - cmSystemTools::CollapseFullPath( - this->Makefile->GetSafeDefinition("CTEST_SOURCE_DIRECTORY")).c_str()); - } - if ( this->CTest->GetCTestConfiguration("BuildDirectory").empty() ) { this->SetError("Build directory not specified. Either use BUILD " diff --git a/Source/CTest/cmCTestHandlerCommand.cxx b/Source/CTest/cmCTestHandlerCommand.cxx index 453e32c..2e2feb0 100644 --- a/Source/CTest/cmCTestHandlerCommand.cxx +++ b/Source/CTest/cmCTestHandlerCommand.cxx @@ -70,19 +70,6 @@ bool cmCTestHandlerCommand this->CTest->SetConfigType(ctestConfigType); } - cmCTestLog(this->CTest, DEBUG, "Initialize handler" << std::endl;); - cmCTestGenericHandler* handler = this->InitializeHandler(); - if ( !handler ) - { - cmCTestLog(this->CTest, ERROR_MESSAGE, - "Cannot instantiate test handler " << this->GetName() - << std::endl); - return false; - } - - handler->SetAppendXML(this->AppendXML); - - handler->PopulateCustomVectors(this->Makefile); if ( this->Values[ct_BUILD] ) { this->CTest->SetCTestConfiguration("BuildDirectory", @@ -119,6 +106,20 @@ bool cmCTestHandlerCommand cmSystemTools::CollapseFullPath( this->Makefile->GetSafeDefinition("CTEST_SOURCE_DIRECTORY")).c_str()); } + + cmCTestLog(this->CTest, DEBUG, "Initialize handler" << std::endl;); + cmCTestGenericHandler* handler = this->InitializeHandler(); + if ( !handler ) + { + cmCTestLog(this->CTest, ERROR_MESSAGE, + "Cannot instantiate test handler " << this->GetName() + << std::endl); + return false; + } + + handler->SetAppendXML(this->AppendXML); + + handler->PopulateCustomVectors(this->Makefile); if ( this->Values[ct_SUBMIT_INDEX] ) { if(!this->CTest->GetDropSiteCDash() && this->CTest->GetDartVersion() <= 1) diff --git a/Source/QtDialog/CMakeSetupDialog.cxx b/Source/QtDialog/CMakeSetupDialog.cxx index c0dde1c..4d62f72 100644 --- a/Source/QtDialog/CMakeSetupDialog.cxx +++ b/Source/QtDialog/CMakeSetupDialog.cxx @@ -28,6 +28,7 @@ #include <QShortcut> #include <QKeySequence> #include <QMacInstallDialog.h> +#include <QInputDialog> #include "QCMake.h" #include "QCMakeCacheView.h" @@ -122,6 +123,22 @@ CMakeSetupDialog::CMakeSetupDialog() QObject::connect(this->InstallForCommandLineAction, SIGNAL(triggered(bool)), this, SLOT(doInstallForCommandLine())); #endif + ToolsMenu->addSeparator(); + ToolsMenu->addAction(tr("&Find in Output..."), + this, SLOT(doOutputFindDialog()), + QKeySequence::Find); + ToolsMenu->addAction(tr("Find Next"), + this, SLOT(doOutputFindNext()), + QKeySequence::FindNext); + ToolsMenu->addAction(tr("Find Previous"), + this, SLOT(doOutputFindPrev()), + QKeySequence::FindPrevious); + ToolsMenu->addAction(tr("Goto Next Error"), + this, SLOT(doOutputErrorNext()), + QKeySequence(Qt::Key_F8)); // in Visual Studio + new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_Period), + this, SLOT(doOutputErrorNext())); // in Eclipse + QMenu* OptionsMenu = this->menuBar()->addMenu(tr("&Options")); this->SuppressDevWarningsAction = OptionsMenu->addAction(tr("&Suppress dev Warnings (-Wno-dev)")); @@ -154,10 +171,6 @@ CMakeSetupDialog::CMakeSetupDialog() QObject::connect(a, SIGNAL(triggered(bool)), this, SLOT(doHelp())); - QShortcut* filterShortcut = new QShortcut(QKeySequence::Find, this); - QObject::connect(filterShortcut, SIGNAL(activated()), - this, SLOT(startSearch())); - this->setAcceptDrops(true); // get the saved binary directories @@ -172,6 +185,10 @@ CMakeSetupDialog::CMakeSetupDialog() this->Output->setFont(outputFont); this->ErrorFormat.setForeground(QBrush(Qt::red)); + this->Output->setContextMenuPolicy(Qt::CustomContextMenu); + connect(this->Output, SIGNAL(customContextMenuRequested(const QPoint&)), + this, SLOT(doOutputContextMenu(const QPoint &))); + // start the cmake worker thread this->CMakeThread = new QCMakeThread(this); QObject::connect(this->CMakeThread, SIGNAL(cmakeInitialized()), @@ -637,7 +654,13 @@ void CMakeSetupDialog::showProgress(const QString& /*msg*/, float percent) void CMakeSetupDialog::error(const QString& msg) { this->Output->setCurrentCharFormat(this->ErrorFormat); - this->Output->append(msg); + //QTextEdit will terminate the msg with a ParagraphSeparator, but it also replaces + //all newlines with ParagraphSeparators. By replacing the newlines by ourself, one + //error msg will be one paragraph. + QString paragraph(msg); + paragraph.replace(QLatin1Char('\n'), QChar::LineSeparator); + this->Output->append(paragraph); + } void CMakeSetupDialog::message(const QString& msg) @@ -1149,4 +1172,134 @@ void CMakeSetupDialog::setSearchFilter(const QString& str) this->CacheValues->setSearchFilter(str); } +void CMakeSetupDialog::doOutputContextMenu(const QPoint &pt) +{ + QMenu *menu = this->Output->createStandardContextMenu(); + + menu->addSeparator(); + menu->addAction(tr("Find..."), + this, SLOT(doOutputFindDialog()), QKeySequence::Find); + menu->addAction(tr("Find Next"), + this, SLOT(doOutputFindNext()), QKeySequence::FindNext); + menu->addAction(tr("Find Previous"), + this, SLOT(doOutputFindPrev()), QKeySequence::FindPrevious); + menu->addSeparator(); + menu->addAction(tr("Goto Next Error"), + this, SLOT(doOutputErrorNext()), QKeySequence(Qt::Key_F8)); + + menu->exec(this->Output->mapToGlobal(pt)); + delete menu; +} + +void CMakeSetupDialog::doOutputFindDialog() +{ + QStringList strings(this->FindHistory); + + QString selection = this->Output->textCursor().selectedText(); + if (!selection.isEmpty() && + !selection.contains(QChar::ParagraphSeparator) && + !selection.contains(QChar::LineSeparator)) + { + strings.push_front(selection); + } + + bool ok; + QString search = QInputDialog::getItem(this, tr("Find in Output"), + tr("Find:"), strings, 0, true, &ok); + if (ok && !search.isEmpty()) + { + if (!this->FindHistory.contains(search)) + { + this->FindHistory.push_front(search); + } + doOutputFindNext(); + } +} + +void CMakeSetupDialog::doOutputFindPrev() +{ + doOutputFindNext(false); +} + +void CMakeSetupDialog::doOutputFindNext(bool directionForward) +{ + if (this->FindHistory.isEmpty()) + { + doOutputFindDialog(); //will re-call this function again + return; + } + + QString search = this->FindHistory.front(); + + QTextCursor cursor = this->Output->textCursor(); + QTextDocument* document = this->Output->document(); + QTextDocument::FindFlags flags; + if (!directionForward) + { + flags |= QTextDocument::FindBackward; + } + + cursor = document->find(search, cursor, flags); + + if (cursor.isNull()) + { + // first search found nothing, wrap around and search again + cursor = this->Output->textCursor(); + cursor.movePosition(directionForward ? QTextCursor::Start + : QTextCursor::End); + cursor = document->find(search, cursor, flags); + } + + if (cursor.hasSelection()) + { + this->Output->setTextCursor(cursor); + } +} + +void CMakeSetupDialog::doOutputErrorNext() +{ + QTextCursor cursor = this->Output->textCursor(); + bool atEnd = false; + // move cursor out of current error-block + if (cursor.blockCharFormat() == this->ErrorFormat) + { + atEnd = !cursor.movePosition(QTextCursor::NextBlock); + } + + // move cursor to next error-block + while (cursor.blockCharFormat() != this->ErrorFormat && !atEnd) + { + atEnd = !cursor.movePosition(QTextCursor::NextBlock); + } + + if (atEnd) + { + // first search found nothing, wrap around and search again + atEnd = !cursor.movePosition(QTextCursor::Start); + + // move cursor to next error-block + while (cursor.blockCharFormat() != this->ErrorFormat && !atEnd) + { + atEnd = !cursor.movePosition(QTextCursor::NextBlock); + } + } + + if (!atEnd) + { + cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor); + + QTextCharFormat selectionFormat; + selectionFormat.setBackground(Qt::yellow); + QTextEdit::ExtraSelection extraSelection = {cursor, selectionFormat}; + this->Output->setExtraSelections(QList<QTextEdit::ExtraSelection>() + << extraSelection); + + // make the whole error-block visible + this->Output->setTextCursor(cursor); + + // remove the selection to see the extraSelection + cursor.setPosition(cursor.anchor()); + this->Output->setTextCursor(cursor); + } +} diff --git a/Source/QtDialog/CMakeSetupDialog.h b/Source/QtDialog/CMakeSetupDialog.h index 2599675..963c7d1 100644 --- a/Source/QtDialog/CMakeSetupDialog.h +++ b/Source/QtDialog/CMakeSetupDialog.h @@ -77,6 +77,11 @@ protected slots: bool doConfigureInternal(); bool doGenerateInternal(); void exitLoop(int); + void doOutputContextMenu(const QPoint &); + void doOutputFindDialog(); + void doOutputFindNext(bool directionForward = true); + void doOutputFindPrev(); + void doOutputErrorNext(); protected: @@ -106,6 +111,7 @@ protected: QTextCharFormat MessageFormat; QStringList AddVariableCompletions; + QStringList FindHistory; QEventLoop LocalLoop; diff --git a/Source/QtDialog/CMakeSetupDialog.ui b/Source/QtDialog/CMakeSetupDialog.ui index dc8ee3f..98da249 100644 --- a/Source/QtDialog/CMakeSetupDialog.ui +++ b/Source/QtDialog/CMakeSetupDialog.ui @@ -107,7 +107,10 @@ </sizepolicy> </property> <property name="text"> - <string>Search:</string> + <string>S&earch:</string> + </property> + <property name="buddy"> + <cstring>Search</cstring> </property> </widget> </item> diff --git a/Source/cmBuildCommand.cxx b/Source/cmBuildCommand.cxx index 91d55a5..b6e2569 100644 --- a/Source/cmBuildCommand.cxx +++ b/Source/cmBuildCommand.cxx @@ -122,7 +122,7 @@ bool cmBuildCommand // std::string makecommand = this->Makefile->GetLocalGenerator() ->GetGlobalGenerator()->GenerateBuildCommand - (makeprogram, project_name, 0, target, configuration, true, false); + (makeprogram, project_name, 0, 0, target, configuration, true, false); this->Makefile->AddDefinition(variable, makecommand.c_str()); @@ -153,7 +153,7 @@ bool cmBuildCommand std::string makecommand = this->Makefile->GetLocalGenerator() ->GetGlobalGenerator()->GenerateBuildCommand - (makeprogram.c_str(), this->Makefile->GetProjectName(), 0, + (makeprogram.c_str(), this->Makefile->GetProjectName(), 0, 0, 0, configType.c_str(), true, false); if(cacheValue) diff --git a/Source/cmDocumentCompileDefinitions.h b/Source/cmDocumentCompileDefinitions.h index ef3b3e7..d15bd6d 100644 --- a/Source/cmDocumentCompileDefinitions.h +++ b/Source/cmDocumentCompileDefinitions.h @@ -23,7 +23,7 @@ "in a (configured) header file. Then report the limitation. " \ "Known limitations include:\n" \ " # - broken almost everywhere\n" \ - " ; - broken in VS IDE and Borland Makefiles\n" \ + " ; - broken in VS IDE 7.0 and Borland Makefiles\n" \ " , - broken in VS IDE\n" \ " % - broken in some cases in NMake\n" \ " & | - broken in some cases on MinGW\n" \ diff --git a/Source/cmDocumentVariables.cxx b/Source/cmDocumentVariables.cxx index cbbbcbe..b383265 100644 --- a/Source/cmDocumentVariables.cxx +++ b/Source/cmDocumentVariables.cxx @@ -1270,7 +1270,7 @@ void cmDocumentVariables::DefineVariables(cmake* cm) cm->DefineProperty ("CMAKE_PDB_OUTPUT_DIRECTORY", cmProperty::VARIABLE, - "Where to put all the MS debug symbol files.", + "Where to put all the MS debug symbol files from linker.", "This variable is used to initialize the " "PDB_OUTPUT_DIRECTORY property on all the targets. " "See that target property for additional information.", diff --git a/Source/cmExtraEclipseCDT4Generator.cxx b/Source/cmExtraEclipseCDT4Generator.cxx index 97ab086..6b02e15 100644 --- a/Source/cmExtraEclipseCDT4Generator.cxx +++ b/Source/cmExtraEclipseCDT4Generator.cxx @@ -39,6 +39,7 @@ cmExtraEclipseCDT4Generator this->SupportsVirtualFolders = true; this->GenerateLinkedResources = true; + this->SupportsGmakeErrorParser = true; } //---------------------------------------------------------------------------- @@ -50,7 +51,7 @@ void cmExtraEclipseCDT4Generator entry.Full = "Project files for Eclipse will be created in the top directory. " "In out of source builds, a linked resource to the top level source " - "directory will be created." + "directory will be created. " "Additionally a hierarchy of makefiles is generated into the " "build tree. The appropriate make program can build the project through " "the default make target. A \"make install\" target is also provided."; @@ -77,6 +78,10 @@ void cmExtraEclipseCDT4Generator::Generate() { this->SupportsVirtualFolders = false; } + if (version < 3007) // 3.7 is Indigo + { + this->SupportsGmakeErrorParser = false; + } } } @@ -403,8 +408,17 @@ void cmExtraEclipseCDT4Generator::CreateProjectFile() { fout << "org.eclipse.cdt.core.ICCErrorParser;"; } + + if (this->SupportsGmakeErrorParser) + { + fout << "org.eclipse.cdt.core.GmakeErrorParser;"; + } + else + { + fout << "org.eclipse.cdt.core.MakeErrorParser;"; + } + fout << - "org.eclipse.cdt.core.MakeErrorParser;" "org.eclipse.cdt.core.GCCErrorParser;" "org.eclipse.cdt.core.GASErrorParser;" "org.eclipse.cdt.core.GLDErrorParser;" diff --git a/Source/cmExtraEclipseCDT4Generator.h b/Source/cmExtraEclipseCDT4Generator.h index 31ad68d..b31cce7 100644 --- a/Source/cmExtraEclipseCDT4Generator.h +++ b/Source/cmExtraEclipseCDT4Generator.h @@ -111,6 +111,7 @@ private: bool GenerateSourceProject; bool GenerateLinkedResources; bool SupportsVirtualFolders; + bool SupportsGmakeErrorParser; }; diff --git a/Source/cmGetFilenameComponentCommand.cxx b/Source/cmGetFilenameComponentCommand.cxx index fd1ad60..1d7fefc 100644 --- a/Source/cmGetFilenameComponentCommand.cxx +++ b/Source/cmGetFilenameComponentCommand.cxx @@ -58,7 +58,7 @@ bool cmGetFilenameComponentCommand } std::string storeArgs; std::string programArgs; - if (args[2] == "PATH") + if (args[2] == "DIRECTORY" || args[2] == "PATH") { result = cmSystemTools::GetFilenamePath(filename); } diff --git a/Source/cmGetFilenameComponentCommand.h b/Source/cmGetFilenameComponentCommand.h index f294daa..09af332 100644 --- a/Source/cmGetFilenameComponentCommand.h +++ b/Source/cmGetFilenameComponentCommand.h @@ -64,12 +64,13 @@ public: return " get_filename_component(<VAR> <FileName> <COMP> [CACHE])\n" "Set <VAR> to a component of <FileName>, where <COMP> is one of:\n" - " PATH = Directory without file name\n" + " DIRECTORY = Directory without file name\n" " NAME = File name without directory\n" " EXT = File name longest extension (.b.c from d/a.b.c)\n" " NAME_WE = File name without directory or longest extension\n" " ABSOLUTE = Full path to file\n" " REALPATH = Full path to existing file with symlinks resolved\n" + " PATH = Legacy alias for DIRECTORY (use for CMake <= 2.8.11)\n" "Paths are returned with forward slashes and have no trailing slahes. " "The longest file extension is always considered. " "If the optional CACHE argument is specified, the result variable is " diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx index bff4681..f38ef65 100644 --- a/Source/cmGlobalGenerator.cxx +++ b/Source/cmGlobalGenerator.cxx @@ -1340,11 +1340,13 @@ int cmGlobalGenerator::TryCompile(const char *srcdir, const char *bindir, std::string cmGlobalGenerator ::GenerateBuildCommand(const char* makeProgram, const char *projectName, - const char* additionalOptions, const char *targetName, - const char* config, bool ignoreErrors, bool) + const char *projectDir, const char* additionalOptions, + const char *targetName, const char* config, + bool ignoreErrors, bool) { - // Project name and config are not used yet. + // Project name & dir and config are not used yet. (void)projectName; + (void)projectDir; (void)config; std::string makeCommand = @@ -1411,7 +1413,7 @@ int cmGlobalGenerator::Build( if (clean) { std::string cleanCommand = - this->GenerateBuildCommand(makeCommandCSTR, projectName, + this->GenerateBuildCommand(makeCommandCSTR, projectName, bindir, 0, "clean", config, false, fast); if(output) { @@ -1443,7 +1445,7 @@ int cmGlobalGenerator::Build( // now build std::string makeCommand = - this->GenerateBuildCommand(makeCommandCSTR, projectName, + this->GenerateBuildCommand(makeCommandCSTR, projectName, bindir, extraOptions, target, config, false, fast); if(output) diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h index 11616e0..9427434 100644 --- a/Source/cmGlobalGenerator.h +++ b/Source/cmGlobalGenerator.h @@ -121,9 +121,10 @@ public: virtual std::string GenerateBuildCommand( const char* makeProgram, - const char *projectName, const char* additionalOptions, - const char *targetName, - const char* config, bool ignoreErrors, bool fast); + const char *projectName, const char *projectDir, + const char* additionalOptions, + const char *targetName, const char* config, + bool ignoreErrors, bool fast); ///! Set the CMake instance diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx index fa277b1..fff972e 100644 --- a/Source/cmGlobalNinjaGenerator.cxx +++ b/Source/cmGlobalNinjaGenerator.cxx @@ -523,14 +523,16 @@ bool cmGlobalNinjaGenerator::UsingMinGW = false; std::string cmGlobalNinjaGenerator ::GenerateBuildCommand(const char* makeProgram, const char* projectName, + const char* projectDir, const char* additionalOptions, const char* targetName, const char* config, bool ignoreErrors, bool fast) { - // Project name and config are not used yet. + // Project name & dir and config are not used yet. (void)projectName; + (void)projectDir; (void)config; // Ninja does not have -i equivalent option yet. (void)ignoreErrors; diff --git a/Source/cmGlobalNinjaGenerator.h b/Source/cmGlobalNinjaGenerator.h index c3df7d9..6e93788 100644 --- a/Source/cmGlobalNinjaGenerator.h +++ b/Source/cmGlobalNinjaGenerator.h @@ -191,6 +191,7 @@ public: /// Overloaded methods. @see cmGlobalGenerator::GenerateBuildCommand() virtual std::string GenerateBuildCommand(const char* makeProgram, const char* projectName, + const char* projectDir, const char* additionalOptions, const char* targetName, const char* config, diff --git a/Source/cmGlobalUnixMakefileGenerator3.cxx b/Source/cmGlobalUnixMakefileGenerator3.cxx index e26cca9..88cd6e5 100644 --- a/Source/cmGlobalUnixMakefileGenerator3.cxx +++ b/Source/cmGlobalUnixMakefileGenerator3.cxx @@ -517,11 +517,13 @@ cmGlobalUnixMakefileGenerator3 std::string cmGlobalUnixMakefileGenerator3 ::GenerateBuildCommand(const char* makeProgram, const char *projectName, - const char* additionalOptions, const char *targetName, - const char* config, bool ignoreErrors, bool fast) + const char *projectDir, const char* additionalOptions, + const char *targetName, const char* config, + bool ignoreErrors, bool fast) { - // Project name and config are not used yet. + // Project name & dir and config are not used yet. (void)projectName; + (void)projectDir; (void)config; std::string makeCommand = diff --git a/Source/cmGlobalUnixMakefileGenerator3.h b/Source/cmGlobalUnixMakefileGenerator3.h index 385cdc4..5e9dce3 100644 --- a/Source/cmGlobalUnixMakefileGenerator3.h +++ b/Source/cmGlobalUnixMakefileGenerator3.h @@ -107,7 +107,8 @@ public: // change the build command for speed virtual std::string GenerateBuildCommand (const char* makeProgram, - const char *projectName, const char* additionalOptions, + const char *projectName, const char *projectDir, + const char* additionalOptions, const char *targetName, const char* config, bool ignoreErrors, bool fast); diff --git a/Source/cmGlobalVisualStudio10Generator.cxx b/Source/cmGlobalVisualStudio10Generator.cxx index b8c4939..742ab78 100644 --- a/Source/cmGlobalVisualStudio10Generator.cxx +++ b/Source/cmGlobalVisualStudio10Generator.cxx @@ -14,6 +14,8 @@ #include "cmLocalVisualStudio10Generator.h" #include "cmMakefile.h" #include "cmSourceFile.h" +#include "cmVisualStudioSlnData.h" +#include "cmVisualStudioSlnParser.h" #include "cmake.h" static const char vs10Win32generatorName[] = "Visual Studio 10"; @@ -215,7 +217,7 @@ std::string cmGlobalVisualStudio10Generator::GetUserMacrosRegKeyBase() std::string cmGlobalVisualStudio10Generator ::GenerateBuildCommand(const char* makeProgram, - const char *projectName, + const char *projectName, const char *projectDir, const char* additionalOptions, const char *targetName, const char* config, bool ignoreErrors, bool fast) { @@ -230,7 +232,8 @@ std::string cmGlobalVisualStudio10Generator lowerCaseCommand.find("VCExpress") != std::string::npos) { return cmGlobalVisualStudio7Generator::GenerateBuildCommand(makeProgram, - projectName, additionalOptions, targetName, config, ignoreErrors, fast); + projectName, projectDir, additionalOptions, targetName, config, + ignoreErrors, fast); } // Otherwise, assume MSBuild command line, and construct accordingly. @@ -258,9 +261,38 @@ std::string cmGlobalVisualStudio10Generator } else { + std::string targetProject(targetName); + targetProject += ".vcxproj"; + if (targetProject.find('/') == std::string::npos) + { + // it might be in a subdir + cmVisualStudioSlnParser parser; + cmSlnData slnData; + std::string slnFile; + if (projectDir && *projectDir) + { + slnFile = projectDir; + slnFile += '/'; + slnFile += projectName; + } + else + { + slnFile = projectName; + } + if (parser.ParseFile(slnFile + ".sln", slnData, + cmVisualStudioSlnParser::DataGroupProjects)) + { + if (cmSlnProjectEntry const* proj = + slnData.GetProjectByName(targetName)) + { + targetProject = proj->GetRelativePath(); + cmSystemTools::ConvertToUnixSlashes(targetProject); + } + } + } + makeCommand += " "; + makeCommand += targetProject; makeCommand += " "; - makeCommand += targetName; - makeCommand += ".vcxproj "; } makeCommand += "/p:Configuration="; if(config && strlen(config)) diff --git a/Source/cmGlobalVisualStudio10Generator.h b/Source/cmGlobalVisualStudio10Generator.h index 5926e0f..dbe6044 100644 --- a/Source/cmGlobalVisualStudio10Generator.h +++ b/Source/cmGlobalVisualStudio10Generator.h @@ -32,7 +32,7 @@ public: virtual std::string GenerateBuildCommand(const char* makeProgram, - const char *projectName, + const char *projectName, const char *projectDir, const char* additionalOptions, const char *targetName, const char* config, bool ignoreErrors, bool); diff --git a/Source/cmGlobalVisualStudio6Generator.cxx b/Source/cmGlobalVisualStudio6Generator.cxx index 9f3af71..b3fabda 100644 --- a/Source/cmGlobalVisualStudio6Generator.cxx +++ b/Source/cmGlobalVisualStudio6Generator.cxx @@ -80,12 +80,15 @@ void cmGlobalVisualStudio6Generator::GenerateConfigurations(cmMakefile* mf) std::string cmGlobalVisualStudio6Generator ::GenerateBuildCommand(const char* makeProgram, const char *projectName, + const char *projectDir, const char* additionalOptions, const char *targetName, const char* config, bool ignoreErrors, bool) { + // Visual studio 6 doesn't need project dir + (void) projectDir; // Ingoring errors is not implemented in visual studio 6 (void) ignoreErrors; diff --git a/Source/cmGlobalVisualStudio6Generator.h b/Source/cmGlobalVisualStudio6Generator.h index 40149e9..6bd39ca 100644 --- a/Source/cmGlobalVisualStudio6Generator.h +++ b/Source/cmGlobalVisualStudio6Generator.h @@ -54,6 +54,7 @@ public: */ virtual std::string GenerateBuildCommand(const char* makeProgram, const char *projectName, + const char *projectDir, const char* additionalOptions, const char *targetName, const char* config, diff --git a/Source/cmGlobalVisualStudio7Generator.cxx b/Source/cmGlobalVisualStudio7Generator.cxx index 63cbdb8..8a186e7 100644 --- a/Source/cmGlobalVisualStudio7Generator.cxx +++ b/Source/cmGlobalVisualStudio7Generator.cxx @@ -56,10 +56,12 @@ void cmGlobalVisualStudio7Generator std::string cmGlobalVisualStudio7Generator ::GenerateBuildCommand(const char* makeProgram, - const char *projectName, + const char *projectName, const char *projectDir, const char* additionalOptions, const char *targetName, const char* config, bool ignoreErrors, bool) { + // Visual studio 7 doesn't need project dir + (void) projectDir; // Ingoring errors is not implemented in visual studio 6 (void) ignoreErrors; diff --git a/Source/cmGlobalVisualStudio7Generator.h b/Source/cmGlobalVisualStudio7Generator.h index 6e78620..3ebb408 100644 --- a/Source/cmGlobalVisualStudio7Generator.h +++ b/Source/cmGlobalVisualStudio7Generator.h @@ -55,6 +55,7 @@ public: */ virtual std::string GenerateBuildCommand(const char* makeProgram, const char *projectName, + const char *projectDir, const char* additionalOptions, const char *targetName, const char* config, diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx index ceac564..870bfa1 100644 --- a/Source/cmGlobalXCodeGenerator.cxx +++ b/Source/cmGlobalXCodeGenerator.cxx @@ -260,6 +260,7 @@ void cmGlobalXCodeGenerator::EnableLanguage(std::vector<std::string>const& std::string cmGlobalXCodeGenerator ::GenerateBuildCommand(const char* makeProgram, const char *projectName, + const char *projectDir, const char* additionalOptions, const char *targetName, const char* config, @@ -268,6 +269,7 @@ std::string cmGlobalXCodeGenerator { // Config is not used yet (void) ignoreErrors; + (void) projectDir; // now build the test if(makeProgram == 0 || !strlen(makeProgram)) @@ -839,7 +841,7 @@ GetSourcecodeValueFromFileExtension(const std::string& _ext, // // Already specialized above or we leave sourcecode == "sourcecode" // // which is probably the most correct choice. Extensionless headers, // // for example... Or file types unknown to Xcode that do not map to a - // // valid lastKnownFileType value. + // // valid explicitFileType value. // } return sourcecode; @@ -882,7 +884,7 @@ cmGlobalXCodeGenerator::CreateXCodeFileReferenceFromPath( std::string sourcecode = GetSourcecodeValueFromFileExtension(ext, lang); - fileRef->AddAttribute("lastKnownFileType", + fileRef->AddAttribute("explicitFileType", this->CreateString(sourcecode.c_str())); // Store the file path relative to the top of the source tree. @@ -1003,7 +1005,7 @@ cmGlobalXCodeGenerator::CreateXCodeTargets(cmLocalGenerator* gen, *i, cmtarget); cmXCodeObject* fr = xsf->GetObject("fileRef"); cmXCodeObject* filetype = - fr->GetObject()->GetObject("lastKnownFileType"); + fr->GetObject()->GetObject("explicitFileType"); cmTarget::SourceFileFlags tsFlags = cmtarget.GetTargetSourceFileFlags(*i); diff --git a/Source/cmGlobalXCodeGenerator.h b/Source/cmGlobalXCodeGenerator.h index 131a6e6..fb897b2 100644 --- a/Source/cmGlobalXCodeGenerator.h +++ b/Source/cmGlobalXCodeGenerator.h @@ -55,6 +55,7 @@ public: */ virtual std::string GenerateBuildCommand(const char* makeProgram, const char *projectName, + const char *projectDir, const char* additionalOptions, const char *targetName, const char* config, diff --git a/Source/cmLocalNinjaGenerator.cxx b/Source/cmLocalNinjaGenerator.cxx index d902f4e..294a539 100644 --- a/Source/cmLocalNinjaGenerator.cxx +++ b/Source/cmLocalNinjaGenerator.cxx @@ -302,7 +302,12 @@ void cmLocalNinjaGenerator::AppendCustomCommandLines(const cmCustomCommand *cc, wd = this->GetMakefile()->GetStartOutputDirectory(); cmOStringStream cdCmd; - cdCmd << "cd " << this->ConvertToOutputFormat(wd, SHELL); +#ifdef _WIN32 + std::string cdStr = "cd /D "; +#else + std::string cdStr = "cd "; +#endif + cdCmd << cdStr << this->ConvertToOutputFormat(wd, SHELL); cmdLines.push_back(cdCmd.str()); } for (unsigned i = 0; i != ccg.GetNumberOfCommands(); ++i) { diff --git a/Source/cmLocalVisualStudio7Generator.cxx b/Source/cmLocalVisualStudio7Generator.cxx index 2dfbfb8..59d5ef0 100644 --- a/Source/cmLocalVisualStudio7Generator.cxx +++ b/Source/cmLocalVisualStudio7Generator.cxx @@ -838,16 +838,6 @@ void cmLocalVisualStudio7Generator::WriteConfiguration(std::ostream& fout, targetOptions.OutputPreprocessorDefinitions(fout, "\t\t\t\t", "\n", "CXX"); fout << "\t\t\t\tAssemblerListingLocation=\"" << configName << "\"\n"; fout << "\t\t\t\tObjectFile=\"$(IntDir)\\\"\n"; - if(targetBuilds) - { - // We need to specify a program database file name even for - // non-debug configurations because VS still creates .idb files. - fout << "\t\t\t\tProgramDataBaseFileName=\"" - << this->ConvertToXMLOutputPathSingle( - target.GetPDBDirectory(configName).c_str()) - << "/" - << target.GetPDBName(configName) << "\"\n"; - } fout << "/>\n"; // end of <Tool Name=VCCLCompilerTool tool = "VCCustomBuildTool"; if(this->FortranProject) diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx index 3fb823c..850e5ea 100644 --- a/Source/cmNinjaTargetGenerator.cxx +++ b/Source/cmNinjaTargetGenerator.cxx @@ -569,11 +569,9 @@ cmNinjaTargetGenerator EnsureParentDirectoryExists(objectFileName); std::string objectDir = cmSystemTools::GetFilenamePath(objectFileName); - objectDir = this->GetLocalGenerator()->Convert(objectDir.c_str(), - cmLocalGenerator::START_OUTPUT, - cmLocalGenerator::SHELL); - vars["OBJECT_DIR"] = objectDir; - + vars["OBJECT_DIR"] = this->GetLocalGenerator()->ConvertToOutputFormat( + ConvertToNinjaPath(objectDir.c_str()).c_str(), + cmLocalGenerator::SHELL); this->SetMsvcTargetPdbVariable(vars); diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index 929e7d5..ed8edcc 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -142,7 +142,7 @@ public: std::vector<IncludeDirectoriesEntry*> IncludeDirectoriesEntries; std::vector<cmValueWithOrigin> LinkInterfaceIncludeDirectoriesEntries; - std::vector<IncludeDirectoriesEntry*> + std::map<std::string, std::vector<IncludeDirectoriesEntry*> > CachedLinkInterfaceIncludeDirectoriesEntries; std::map<std::string, std::string> CachedLinkInterfaceCompileDefinitions; @@ -165,6 +165,19 @@ void deleteAndClear( } //---------------------------------------------------------------------------- +void deleteAndClear( + std::map<std::string, + std::vector<cmTargetInternals::IncludeDirectoriesEntry*> > &entries) +{ + for (std::map<std::string, + std::vector<cmTargetInternals::IncludeDirectoriesEntry*> >::iterator + it = entries.begin(), end = entries.end(); it != end; ++it) + { + deleteAndClear(it->second); + } +} + +//---------------------------------------------------------------------------- cmTargetInternals::~cmTargetInternals() { deleteAndClear(CachedLinkInterfaceIncludeDirectoriesEntries); @@ -873,9 +886,9 @@ void cmTarget::DefineProperties(cmake *cm) cm->DefineProperty ("PDB_NAME", cmProperty::TARGET, - "Output name for MS debug symbols .pdb file.", + "Output name for MS debug symbols .pdb file from linker.", "Set the base name for debug symbols file created for an " - "executable or library target. " + "executable or shared library target. " "If not set, the logical target name is used by default. " "\n" "This property is not implemented by the Visual Studio 6 generator."); @@ -1384,9 +1397,9 @@ void cmTarget::DefineProperties(cmake *cm) cm->DefineProperty ("PDB_OUTPUT_DIRECTORY", cmProperty::TARGET, - "Output directory for MS debug symbols .pdb files.", + "Output directory for MS debug symbols .pdb file from linker.", "This property specifies the directory into which the MS debug symbols " - "will be placed. " + "will be placed by the linker. " "This property is initialized by the value of the variable " "CMAKE_PDB_OUTPUT_DIRECTORY if it is set when a target is created." "\n" @@ -2706,6 +2719,19 @@ void cmTarget::SetProperty(const char* prop, const char* value) new cmTargetInternals::IncludeDirectoriesEntry(cge)); return; } + if (strcmp(prop, "LINK_LIBRARIES") == 0) + { + this->Internal->LinkInterfaceIncludeDirectoriesEntries.clear(); + if (cmGeneratorExpression::IsValidTargetName(value) + || cmGeneratorExpression::Find(value) != std::string::npos) + { + cmListFileBacktrace lfbt; + this->Makefile->GetBacktrace(lfbt); + cmValueWithOrigin entry(value, lfbt); + this->Internal->LinkInterfaceIncludeDirectoriesEntries.push_back(entry); + } + // Fall through + } this->Properties.SetProperty(prop, value, cmProperty::TARGET); this->MaybeInvalidatePropertyCache(prop); } @@ -2727,6 +2753,18 @@ void cmTarget::AppendProperty(const char* prop, const char* value, new cmTargetInternals::IncludeDirectoriesEntry(ge.Parse(value))); return; } + if (strcmp(prop, "LINK_LIBRARIES") == 0) + { + if (cmGeneratorExpression::IsValidTargetName(value) + || cmGeneratorExpression::Find(value) != std::string::npos) + { + cmListFileBacktrace lfbt; + this->Makefile->GetBacktrace(lfbt); + cmValueWithOrigin entry(value, lfbt); + this->Internal->LinkInterfaceIncludeDirectoriesEntries.push_back(entry); + } + // Fall through + } this->Properties.AppendProperty(prop, value, cmProperty::TARGET, asString); this->MaybeInvalidatePropertyCache(prop); } @@ -2763,12 +2801,6 @@ void cmTarget::AppendBuildInterfaceIncludes() } //---------------------------------------------------------------------------- -void cmTarget::AppendTllInclude(const cmValueWithOrigin &entry) -{ - this->Internal->LinkInterfaceIncludeDirectoriesEntries.push_back(entry); -} - -//---------------------------------------------------------------------------- void cmTarget::InsertInclude(const cmValueWithOrigin &entry, bool before) { @@ -2939,25 +2971,33 @@ std::vector<std::string> cmTarget::GetIncludeDirectories(const char *config) ge.Parse(it->Value); std::string result = cge->Evaluate(this->Makefile, config, false, this, 0, 0); - if (!cmGeneratorExpression::IsValidTargetName(result.c_str()) - || !this->Makefile->FindTargetToUse(result.c_str())) + if (!this->Makefile->FindTargetToUse(result.c_str())) { continue; } } + std::string includeGenex = "$<TARGET_PROPERTY:" + + it->Value + ",INTERFACE_INCLUDE_DIRECTORIES>"; + if (cmGeneratorExpression::Find(it->Value) != std::string::npos) + { + // Because it->Value is a generator expression, ensure that it + // evaluates to the non-empty string before being used in the + // TARGET_PROPERTY expression. + includeGenex = "$<$<BOOL:" + it->Value + ">:" + includeGenex + ">"; + } cmGeneratorExpression ge(it->Backtrace); cmsys::auto_ptr<cmCompiledGeneratorExpression> cge = ge.Parse( - "$<TARGET_PROPERTY:" + - it->Value + ",INTERFACE_INCLUDE_DIRECTORIES>"); + includeGenex); - this->Internal->CachedLinkInterfaceIncludeDirectoriesEntries.push_back( + this->Internal + ->CachedLinkInterfaceIncludeDirectoriesEntries[configString].push_back( new cmTargetInternals::IncludeDirectoriesEntry(cge, it->Value)); } } processIncludeDirectories(this, - this->Internal->CachedLinkInterfaceIncludeDirectoriesEntries, + this->Internal->CachedLinkInterfaceIncludeDirectoriesEntries[configString], includes, uniqueIncludes, &dagChecker, @@ -2967,7 +3007,7 @@ std::vector<std::string> cmTarget::GetIncludeDirectories(const char *config) if (!this->Makefile->IsGeneratingBuildSystem()) { deleteAndClear( - this->Internal->CachedLinkInterfaceIncludeDirectoriesEntries); + this->Internal->CachedLinkInterfaceIncludeDirectoriesEntries); } else { diff --git a/Source/cmTarget.h b/Source/cmTarget.h index e25133e..9d46796 100644 --- a/Source/cmTarget.h +++ b/Source/cmTarget.h @@ -495,7 +495,6 @@ public: std::vector<std::string> GetIncludeDirectories(const char *config); void InsertInclude(const cmValueWithOrigin &entry, bool before = false); - void AppendTllInclude(const cmValueWithOrigin &entry); void AppendBuildInterfaceIncludes(); diff --git a/Source/cmTargetLinkLibrariesCommand.cxx b/Source/cmTargetLinkLibrariesCommand.cxx index 3f652c9..b7b7691 100644 --- a/Source/cmTargetLinkLibrariesCommand.cxx +++ b/Source/cmTargetLinkLibrariesCommand.cxx @@ -259,14 +259,6 @@ cmTargetLinkLibrariesCommand::HandleLibrary(const char* lib, // Handle normal case first. if(this->CurrentProcessingState != ProcessingLinkInterface) { - { - cmListFileBacktrace lfbt; - this->Makefile->GetBacktrace(lfbt); - cmValueWithOrigin entry(this->Target->GetDebugGeneratorExpressions(lib, - llt), - lfbt); - this->Target->AppendTllInclude(entry); - } this->Makefile ->AddLinkLibraryForTarget(this->Target->GetName(), lib, llt); if (this->CurrentProcessingState != ProcessingPublicInterface) diff --git a/Source/cmTargetLinkLibrariesCommand.h b/Source/cmTargetLinkLibrariesCommand.h index aaabdfa..c683016 100644 --- a/Source/cmTargetLinkLibrariesCommand.h +++ b/Source/cmTargetLinkLibrariesCommand.h @@ -97,14 +97,17 @@ public: "Calls to other signatures of this command may set the property " "making any libraries linked exclusively by this signature private." "\n" - "Target usage requirements are also consumed by this command. If the " - "<target> is linked to another target which has " - "a populated INTERFACE_INCLUDE_DIRECTORIES, the content of it is " - "appended to the INCLUDE_DIRECTORIES of <target>. Similarly, the " - "INTERFACE_COMPILE_DEFINITONS of a dependee are added to the " - "COMPILE_DEFINITONS of <target>, and the " - "INTERFACE_POSITION_INDEPENDENT_CODE property is used to determine the " - "POSITION_INDEPENDENT_CODE property of <target>." + "CMake will also propagate \"usage requirements\" from linked library " + "targets. " + "Usage requirements affect compilation of sources in the <target>. " + "They are specified by properties defined on linked targets. " + "During generation of the build system, CMake integrates " + "usage requirement property values with the corresponding " + "build properties for <target>:\n" + " INTERFACE_COMPILE_DEFINITONS: Appends to COMPILE_DEFINITONS\n" + " INTERFACE_INCLUDE_DIRECTORIES: Appends to INCLUDE_DIRECTORIES\n" + " INTERFACE_POSITION_INDEPENDENT_CODE: Sets POSITION_INDEPENDENT_CODE\n" + " or checked for consistency with existing value\n" "\n" " target_link_libraries(<target> LINK_INTERFACE_LIBRARIES\n" " [[debug|optimized|general] <lib>] ...)\n" diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx index e4c508c..5e8f76d 100644 --- a/Source/cmVisualStudio10TargetGenerator.cxx +++ b/Source/cmVisualStudio10TargetGenerator.cxx @@ -1264,14 +1264,6 @@ void cmVisualStudio10TargetGenerator::WriteClOptions( *this->BuildFileStream << configName << "</AssemblerListingLocation>\n"; this->WriteString("<ObjectFileName>$(IntDir)</ObjectFileName>\n", 3); - if(this->Target->GetType() != cmTarget::OBJECT_LIBRARY) - { - this->WriteString("<ProgramDataBaseFileName>", 3); - *this->BuildFileStream << this->Target->GetPDBDirectory(configName.c_str()) - << "/" - << this->Target->GetPDBName(configName.c_str()) - << "</ProgramDataBaseFileName>\n"; - } this->WriteString("</ClCompile>\n", 2); } diff --git a/Source/cmVisualStudioGeneratorOptions.cxx b/Source/cmVisualStudioGeneratorOptions.cxx index 3579997..1c1988a 100644 --- a/Source/cmVisualStudioGeneratorOptions.cxx +++ b/Source/cmVisualStudioGeneratorOptions.cxx @@ -6,6 +6,7 @@ inline std::string cmVisualStudio10GeneratorOptionsEscapeForXML(const char* s) { std::string ret = s; + cmSystemTools::ReplaceString(ret, ";", "%3B"); cmSystemTools::ReplaceString(ret, "&", "&"); cmSystemTools::ReplaceString(ret, "<", "<"); cmSystemTools::ReplaceString(ret, ">", ">"); diff --git a/Source/cmVisualStudioSlnData.cxx b/Source/cmVisualStudioSlnData.cxx new file mode 100644 index 0000000..82b4ee8 --- /dev/null +++ b/Source/cmVisualStudioSlnData.cxx @@ -0,0 +1,62 @@ +/*============================================================================ + 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; +} diff --git a/Source/cmVisualStudioSlnData.h b/Source/cmVisualStudioSlnData.h new file mode 100644 index 0000000..ec128cf --- /dev/null +++ b/Source/cmVisualStudioSlnData.h @@ -0,0 +1,58 @@ +/*============================================================================ + 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 diff --git a/Source/cmVisualStudioSlnParser.cxx b/Source/cmVisualStudioSlnParser.cxx new file mode 100644 index 0000000..bae5974 --- /dev/null +++ b/Source/cmVisualStudioSlnParser.cxx @@ -0,0 +1,712 @@ +/*============================================================================ + 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 "cmVisualStudioSlnParser.h" + +#include "cmSystemTools.h" +#include "cmVisualStudioSlnData.h" + +#include <cassert> +#include <stack> + +//---------------------------------------------------------------------------- +namespace +{ + enum LineFormat + { + LineMultiValueTag, + LineSingleValueTag, + LineKeyValuePair, + LineVerbatim + }; +} + +//---------------------------------------------------------------------------- +class cmVisualStudioSlnParser::ParsedLine +{ +public: + bool IsComment() const; + bool IsKeyValuePair() const; + + const std::string& GetTag() const { return this->Tag; } + const std::string& GetArg() const { return this->Arg.first; } + std::string GetArgVerbatim() const; + size_t GetValueCount() const { return this->Values.size(); } + const std::string& GetValue(size_t idxValue) const; + std::string GetValueVerbatim(size_t idxValue) const; + + void SetTag(const std::string& tag) { this->Tag = tag; } + void SetArg(const std::string& arg) { this->Arg = StringData(arg, false); } + void SetQuotedArg(const std::string& arg) + { this->Arg = StringData(arg, true); } + void AddValue(const std::string& value) + { this->Values.push_back(StringData(value, false)); } + void AddQuotedValue(const std::string& value) + { this->Values.push_back(StringData(value, true)); } + + void CopyVerbatim(const std::string& line) { this->Tag = line; } + +private: + typedef std::pair<std::string, bool> StringData; + std::string Tag; + StringData Arg; + std::vector<StringData> Values; + static const std::string BadString; + static const std::string Quote; +}; + +//---------------------------------------------------------------------------- +const std::string cmVisualStudioSlnParser::ParsedLine::BadString; +const std::string cmVisualStudioSlnParser::ParsedLine::Quote("\""); + +//---------------------------------------------------------------------------- +bool cmVisualStudioSlnParser::ParsedLine::IsComment() const +{ + assert(!this->Tag.empty()); + return (this->Tag[0]== '#'); +} + +//---------------------------------------------------------------------------- +bool cmVisualStudioSlnParser::ParsedLine::IsKeyValuePair() const +{ + assert(!this->Tag.empty()); + return this->Arg.first.empty() && this->Values.size() == 1; +} + +//---------------------------------------------------------------------------- +std::string cmVisualStudioSlnParser::ParsedLine::GetArgVerbatim() const +{ + if (this->Arg.second) + return Quote + this->Arg.first + Quote; + else + return this->Arg.first; +} + +//---------------------------------------------------------------------------- +const std::string& +cmVisualStudioSlnParser::ParsedLine::GetValue(size_t idxValue) const +{ + if (idxValue < this->Values.size()) + return this->Values[idxValue].first; + else + return BadString; +} + +//---------------------------------------------------------------------------- +std::string +cmVisualStudioSlnParser::ParsedLine::GetValueVerbatim(size_t idxValue) const +{ + if (idxValue < this->Values.size()) + { + const StringData& data = this->Values[idxValue]; + if (data.second) + return Quote + data.first + Quote; + else + return data.first; + } + else + return BadString; +} + +//---------------------------------------------------------------------------- +class cmVisualStudioSlnParser::State +{ +public: + explicit State(DataGroupSet requestedData); + + size_t GetCurrentLine() const { return this->CurrentLine; } + bool ReadLine(std::istream& input, std::string& line); + + LineFormat NextLineFormat() const; + + bool Process(const cmVisualStudioSlnParser::ParsedLine& line, + cmSlnData& output, + cmVisualStudioSlnParser::ResultData& result); + + bool Finished(cmVisualStudioSlnParser::ResultData& result); + +private: + enum FileState + { + FileStateStart, + FileStateTopLevel, + FileStateProject, + FileStateProjectDependencies, + FileStateGlobal, + FileStateSolutionConfigurations, + FileStateProjectConfigurations, + FileStateSolutionFilters, + FileStateGlobalSection, + FileStateIgnore + }; + std::stack<FileState> Stack; + std::string EndIgnoreTag; + DataGroupSet RequestedData; + size_t CurrentLine; + + void IgnoreUntilTag(const std::string& endTag); +}; + +//---------------------------------------------------------------------------- +cmVisualStudioSlnParser::State::State(DataGroupSet requestedData) : + RequestedData(requestedData), + CurrentLine(0) +{ + if (this->RequestedData.test(DataGroupProjectDependenciesBit)) + this->RequestedData.set(DataGroupProjectsBit); + this->Stack.push(FileStateStart); +} + +//---------------------------------------------------------------------------- +bool cmVisualStudioSlnParser::State::ReadLine(std::istream& input, + std::string& line) +{ + ++this->CurrentLine; + return !std::getline(input, line).fail(); +} + +//---------------------------------------------------------------------------- +LineFormat cmVisualStudioSlnParser::State::NextLineFormat() const +{ + switch (this->Stack.top()) + { + case FileStateStart: return LineVerbatim; + case FileStateTopLevel: return LineMultiValueTag; + case FileStateProject: return LineSingleValueTag; + case FileStateProjectDependencies: return LineKeyValuePair; + case FileStateGlobal: return LineSingleValueTag; + case FileStateSolutionConfigurations: return LineKeyValuePair; + case FileStateProjectConfigurations: return LineKeyValuePair; + case FileStateSolutionFilters: return LineKeyValuePair; + case FileStateGlobalSection: return LineKeyValuePair; + case FileStateIgnore: return LineVerbatim; + default: + assert(false); + return LineVerbatim; + } +} + +//---------------------------------------------------------------------------- +bool cmVisualStudioSlnParser::State::Process( + const cmVisualStudioSlnParser::ParsedLine& line, + cmSlnData& output, cmVisualStudioSlnParser::ResultData& result) +{ + assert(!line.IsComment()); + switch (this->Stack.top()) + { + case FileStateStart: + if (!cmSystemTools::StringStartsWith( + line.GetTag().c_str(), "Microsoft Visual Studio Solution File")) + { + result.SetError(ResultErrorInputStructure, this->GetCurrentLine()); + return false; + } + this->Stack.pop(); + this->Stack.push(FileStateTopLevel); + break; + case FileStateTopLevel: + if (line.GetTag().compare("Project") == 0) + { + if (line.GetValueCount() != 3) + { + result.SetError(ResultErrorInputStructure, this->GetCurrentLine()); + return false; + } + if (this->RequestedData.test(DataGroupProjectsBit)) + { + if (!output.AddProject(line.GetValue(2), + line.GetValue(0), + line.GetValue(1))) + { + result.SetError(ResultErrorInputData, this->GetCurrentLine()); + return false; + } + this->Stack.push(FileStateProject); + } + else + this->IgnoreUntilTag("EndProject"); + } + else if (line.GetTag().compare("Global") == 0) + this->Stack.push(FileStateGlobal); + else + { + result.SetError(ResultErrorInputStructure, this->GetCurrentLine()); + return false; + } + break; + case FileStateProject: + if (line.GetTag().compare("EndProject") == 0) + this->Stack.pop(); + else if (line.GetTag().compare("ProjectSection") == 0) + { + if (line.GetArg().compare("ProjectDependencies") == 0 && + line.GetValue(0).compare("postProject") == 0) + { + if (this->RequestedData.test(DataGroupProjectDependenciesBit)) + this->Stack.push(FileStateProjectDependencies); + else + this->IgnoreUntilTag("EndProjectSection"); + } + else + this->IgnoreUntilTag("EndProjectSection"); + } + else + { + result.SetError(ResultErrorInputStructure, this->GetCurrentLine()); + return false; + } + break; + case FileStateProjectDependencies: + if (line.GetTag().compare("EndProjectSection") == 0) + this->Stack.pop(); + else if (line.IsKeyValuePair()) + // implement dependency storing here, once needed + ; + else + { + result.SetError(ResultErrorInputStructure, this->GetCurrentLine()); + return false; + } + break; + case FileStateGlobal: + if (line.GetTag().compare("EndGlobal") == 0) + this->Stack.pop(); + else if (line.GetTag().compare("GlobalSection") == 0) + { + if (line.GetArg().compare("SolutionConfigurationPlatforms") == 0 && + line.GetValue(0).compare("preSolution") == 0) + { + if (this->RequestedData.test(DataGroupSolutionConfigurationsBit)) + this->Stack.push(FileStateSolutionConfigurations); + else + this->IgnoreUntilTag("EndGlobalSection"); + } + else if (line.GetArg().compare("ProjectConfigurationPlatforms") == 0 && + line.GetValue(0).compare("postSolution") == 0) + { + if (this->RequestedData.test(DataGroupProjectConfigurationsBit)) + this->Stack.push(FileStateProjectConfigurations); + else + this->IgnoreUntilTag("EndGlobalSection"); + } + else if (line.GetArg().compare("NestedProjects") == 0 && + line.GetValue(0).compare("preSolution") == 0) + { + if (this->RequestedData.test(DataGroupSolutionFiltersBit)) + this->Stack.push(FileStateSolutionFilters); + else + this->IgnoreUntilTag("EndGlobalSection"); + } + else if (this->RequestedData.test(DataGroupGenericGlobalSectionsBit)) + this->Stack.push(FileStateGlobalSection); + else + this->IgnoreUntilTag("EndGlobalSection"); + } + else + { + result.SetError(ResultErrorInputStructure, this->GetCurrentLine()); + return false; + } + break; + case FileStateSolutionConfigurations: + if (line.GetTag().compare("EndGlobalSection") == 0) + this->Stack.pop(); + else if (line.IsKeyValuePair()) + // implement configuration storing here, once needed + ; + else + { + result.SetError(ResultErrorInputStructure, this->GetCurrentLine()); + return false; + } + break; + case FileStateProjectConfigurations: + if (line.GetTag().compare("EndGlobalSection") == 0) + this->Stack.pop(); + else if (line.IsKeyValuePair()) + // implement configuration storing here, once needed + ; + else + { + result.SetError(ResultErrorInputStructure, this->GetCurrentLine()); + return false; + } + break; + case FileStateSolutionFilters: + if (line.GetTag().compare("EndGlobalSection") == 0) + this->Stack.pop(); + else if (line.IsKeyValuePair()) + // implement filter storing here, once needed + ; + else + { + result.SetError(ResultErrorInputStructure, this->GetCurrentLine()); + return false; + } + break; + case FileStateGlobalSection: + if (line.GetTag().compare("EndGlobalSection") == 0) + this->Stack.pop(); + else if (line.IsKeyValuePair()) + // implement section storing here, once needed + ; + else + { + result.SetError(ResultErrorInputStructure, this->GetCurrentLine()); + return false; + } + break; + case FileStateIgnore: + if (line.GetTag() == this->EndIgnoreTag) + { + this->Stack.pop(); + this->EndIgnoreTag = ""; + } + break; + default: + result.SetError(ResultErrorBadInternalState, this->GetCurrentLine()); + return false; + } + return true; +} + +//---------------------------------------------------------------------------- +bool cmVisualStudioSlnParser::State::Finished( + cmVisualStudioSlnParser::ResultData& result) +{ + if (this->Stack.top() != FileStateTopLevel) + { + result.SetError(ResultErrorInputStructure, this->GetCurrentLine()); + return false; + } + result.Result = ResultOK; + return true; +} + +//---------------------------------------------------------------------------- +void cmVisualStudioSlnParser::State::IgnoreUntilTag(const std::string& endTag) +{ + this->Stack.push(FileStateIgnore); + this->EndIgnoreTag = endTag; +} + +//---------------------------------------------------------------------------- +cmVisualStudioSlnParser::ResultData::ResultData() + : Result(ResultOK) + , ResultLine(0) +{} + +//---------------------------------------------------------------------------- +void cmVisualStudioSlnParser::ResultData::Clear() +{ + *this = ResultData(); +} + +//---------------------------------------------------------------------------- +void cmVisualStudioSlnParser::ResultData::SetError(ParseResult error, + size_t line) +{ + this->Result = error; + this->ResultLine = line; +} + +//---------------------------------------------------------------------------- +const cmVisualStudioSlnParser::DataGroupSet +cmVisualStudioSlnParser::DataGroupProjects( + 1 << cmVisualStudioSlnParser::DataGroupProjectsBit); + +const cmVisualStudioSlnParser::DataGroupSet +cmVisualStudioSlnParser::DataGroupProjectDependencies( + 1 << cmVisualStudioSlnParser::DataGroupProjectDependenciesBit); + +const cmVisualStudioSlnParser::DataGroupSet +cmVisualStudioSlnParser::DataGroupSolutionConfigurations( + 1 << cmVisualStudioSlnParser::DataGroupSolutionConfigurationsBit); + +const cmVisualStudioSlnParser::DataGroupSet +cmVisualStudioSlnParser::DataGroupProjectConfigurations( + 1 << cmVisualStudioSlnParser::DataGroupProjectConfigurationsBit); + +const cmVisualStudioSlnParser::DataGroupSet +cmVisualStudioSlnParser::DataGroupSolutionFilters( + 1 << cmVisualStudioSlnParser::DataGroupSolutionFiltersBit); + +const cmVisualStudioSlnParser::DataGroupSet +cmVisualStudioSlnParser::DataGroupGenericGlobalSections( + 1 << cmVisualStudioSlnParser::DataGroupGenericGlobalSectionsBit); + +const cmVisualStudioSlnParser::DataGroupSet +cmVisualStudioSlnParser::DataGroupAll(~0); + +//---------------------------------------------------------------------------- +bool cmVisualStudioSlnParser::Parse(std::istream& input, + cmSlnData& output, + DataGroupSet dataGroups) +{ + this->LastResult.Clear(); + if (!this->IsDataGroupSetSupported(dataGroups)) + { + this->LastResult.SetError(ResultErrorUnsupportedDataGroup, 0); + return false; + } + State state(dataGroups); + return this->ParseImpl(input, output, state); +} + +//---------------------------------------------------------------------------- +bool cmVisualStudioSlnParser::ParseFile(const std::string& file, + cmSlnData& output, + DataGroupSet dataGroups) +{ + this->LastResult.Clear(); + if (!this->IsDataGroupSetSupported(dataGroups)) + { + this->LastResult.SetError(ResultErrorUnsupportedDataGroup, 0); + return false; + } + std::ifstream f(file.c_str()); + if (!f) + { + this->LastResult.SetError(ResultErrorOpeningInput, 0); + return false; + } + State state(dataGroups); + return this->ParseImpl(f, output, state); +} + +//---------------------------------------------------------------------------- +cmVisualStudioSlnParser::ParseResult +cmVisualStudioSlnParser::GetParseResult() const +{ + return this->LastResult.Result; +} + +//---------------------------------------------------------------------------- +size_t cmVisualStudioSlnParser::GetParseResultLine() const +{ + return this->LastResult.ResultLine; +} + +//---------------------------------------------------------------------------- +bool cmVisualStudioSlnParser::GetParseHadBOM() const +{ + return this->LastResult.HadBOM; +} + +//---------------------------------------------------------------------------- +bool +cmVisualStudioSlnParser::IsDataGroupSetSupported(DataGroupSet dataGroups) const +{ + return (dataGroups & DataGroupProjects) == dataGroups; + //only supporting DataGroupProjects for now +} + +//---------------------------------------------------------------------------- +bool cmVisualStudioSlnParser::ParseImpl(std::istream& input, + cmSlnData& output, + State& state) +{ + std::string line; + // Does the .sln start with a Byte Order Mark? + if (!this->ParseBOM(input, line, state)) + return false; + do + { + line = cmSystemTools::TrimWhitespace(line); + if (line.empty()) + continue; + ParsedLine parsedLine; + switch (state.NextLineFormat()) + { + case LineMultiValueTag: + if (!this->ParseMultiValueTag(line, parsedLine, state)) + return false; + break; + case LineSingleValueTag: + if (!this->ParseSingleValueTag(line, parsedLine, state)) + return false; + break; + case LineKeyValuePair: + if (!this->ParseKeyValuePair(line, parsedLine, state)) + return false; + break; + case LineVerbatim: + parsedLine.CopyVerbatim(line); + break; + } + if (parsedLine.IsComment()) + continue; + if (!state.Process(parsedLine, output, this->LastResult)) + return false; + } + while (state.ReadLine(input, line)); + return state.Finished(this->LastResult); +} + +//---------------------------------------------------------------------------- +bool cmVisualStudioSlnParser::ParseBOM(std::istream& input, + std::string& line, + State& state) +{ + char bom[4]; + if (!input.get(bom, 4)) + { + this->LastResult.SetError(ResultErrorReadingInput, 1); + return false; + } + this->LastResult.HadBOM = + (bom[0] == char(0xEF) && bom[1] == char(0xBB) && bom[2] == char(0xBF)); + if (!state.ReadLine(input, line)) + { + this->LastResult.SetError(ResultErrorReadingInput, 1); + return false; + } + if (!this->LastResult.HadBOM) + line = bom + line; // it wasn't a BOM, prepend it to first line + return true; +} + +//---------------------------------------------------------------------------- +bool cmVisualStudioSlnParser::ParseMultiValueTag(const std::string& line, + ParsedLine& parsedLine, + State& state) +{ + size_t idxEqualSign = line.find('='); + const std::string& fullTag = line.substr(0, idxEqualSign); + if (!this->ParseTag(fullTag, parsedLine, state)) + return false; + if (idxEqualSign != line.npos) + { + size_t idxFieldStart = idxEqualSign + 1; + if (idxFieldStart < line.size()) + { + size_t idxParsing = idxFieldStart; + bool inQuotes = false; + for (;;) + { + idxParsing = line.find_first_of(",\"", idxParsing); + bool fieldOver = false; + if (idxParsing == line.npos) + { + fieldOver = true; + if (inQuotes) + { + this->LastResult.SetError(ResultErrorInputStructure, + state.GetCurrentLine()); + return false; + } + } + else if (line[idxParsing] == ',' && !inQuotes) + fieldOver = true; + else if (line[idxParsing] == '"') + inQuotes = !inQuotes; + if (fieldOver) + { + if (!this->ParseValue(line.substr(idxFieldStart, + idxParsing - idxFieldStart), + parsedLine)) + return false; + if (idxParsing == line.npos) + break; //end of last field + idxFieldStart = idxParsing + 1; + } + ++idxParsing; + } + } + } + return true; +} + +//---------------------------------------------------------------------------- +bool cmVisualStudioSlnParser::ParseSingleValueTag(const std::string& line, + ParsedLine& parsedLine, + State& state) +{ + size_t idxEqualSign = line.find('='); + const std::string& fullTag = line.substr(0, idxEqualSign); + if (!this->ParseTag(fullTag, parsedLine, state)) + return false; + if (idxEqualSign != line.npos) + { + if (!this->ParseValue(line.substr(idxEqualSign + 1), parsedLine)) + return false; + } + return true; +} + +//---------------------------------------------------------------------------- +bool cmVisualStudioSlnParser::ParseKeyValuePair(const std::string& line, + ParsedLine& parsedLine, + State& /*state*/) +{ + size_t idxEqualSign = line.find('='); + if (idxEqualSign == line.npos) + { + parsedLine.CopyVerbatim(line); + return true; + } + const std::string& key = line.substr(0, idxEqualSign); + parsedLine.SetTag(cmSystemTools::TrimWhitespace(key)); + const std::string& value = line.substr(idxEqualSign + 1); + parsedLine.AddValue(cmSystemTools::TrimWhitespace(value)); + return true; +} + +//---------------------------------------------------------------------------- +bool cmVisualStudioSlnParser::ParseTag(const std::string& fullTag, + ParsedLine& parsedLine, + State& state) +{ + size_t idxLeftParen = fullTag.find('('); + if (idxLeftParen == fullTag.npos) + { + parsedLine.SetTag(cmSystemTools::TrimWhitespace(fullTag)); + return true; + } + parsedLine.SetTag( + cmSystemTools::TrimWhitespace(fullTag.substr(0, idxLeftParen))); + size_t idxRightParen = fullTag.rfind(')'); + if (idxRightParen == fullTag.npos) + { + this->LastResult.SetError(ResultErrorInputStructure, + state.GetCurrentLine()); + return false; + } + const std::string& arg = cmSystemTools::TrimWhitespace( + fullTag.substr(idxLeftParen + 1, idxRightParen - idxLeftParen - 1)); + if (arg[0] == '"') + { + if (arg[arg.size() - 1] != '"') + { + this->LastResult.SetError(ResultErrorInputStructure, + state.GetCurrentLine()); + return false; + } + parsedLine.SetQuotedArg(arg.substr(1, arg.size() - 2)); + } + else + parsedLine.SetArg(arg); + return true; +} + +//---------------------------------------------------------------------------- +bool cmVisualStudioSlnParser::ParseValue(const std::string& value, + ParsedLine& parsedLine) +{ + const std::string& trimmed = cmSystemTools::TrimWhitespace(value); + if (trimmed.empty()) + parsedLine.AddValue(trimmed); + else if (trimmed[0] == '"' && trimmed[trimmed.size() - 1] == '"') + parsedLine.AddQuotedValue(trimmed.substr(1, trimmed.size() - 2)); + else + parsedLine.AddValue(trimmed); + return true; +} diff --git a/Source/cmVisualStudioSlnParser.h b/Source/cmVisualStudioSlnParser.h new file mode 100644 index 0000000..bee70cc --- /dev/null +++ b/Source/cmVisualStudioSlnParser.h @@ -0,0 +1,118 @@ +/*============================================================================ + 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 |