#include "SolutionParserVisualStudio.h" #include #include #include #include "boost/filesystem/path.hpp" #include "boost/filesystem.hpp" #include "SolutionParserUtility.h" #include "utility/logging/logging.h" SolutionParserVisualStudio::SolutionParserVisualStudio() : m_compatibilityFlags() { m_compatibilityFlags.push_back("-fms-extensions"); m_compatibilityFlags.push_back("-fms-compatibility"); m_compatibilityFlags.push_back("-fms-compatibility-version=19.00"); SolutionParserUtility::m_ideMacros.clear(); SolutionParserUtility::m_ideMacros.push_back("RemoteMachine"); SolutionParserUtility::m_ideMacros.push_back("Configuration"); SolutionParserUtility::m_ideMacros.push_back("Platform"); SolutionParserUtility::m_ideMacros.push_back("ParentName"); SolutionParserUtility::m_ideMacros.push_back("RootNameSpace"); SolutionParserUtility::m_ideMacros.push_back("IntDir"); SolutionParserUtility::m_ideMacros.push_back("OutDir"); SolutionParserUtility::m_ideMacros.push_back("DevEnvDir"); SolutionParserUtility::m_ideMacros.push_back("InputPath"); SolutionParserUtility::m_ideMacros.push_back("InputName"); SolutionParserUtility::m_ideMacros.push_back("InputFileName"); SolutionParserUtility::m_ideMacros.push_back("InputExt"); SolutionParserUtility::m_ideMacros.push_back("ProjectDir"); SolutionParserUtility::m_ideMacros.push_back("ProjectPath"); SolutionParserUtility::m_ideMacros.push_back("ProjectName"); SolutionParserUtility::m_ideMacros.push_back("ProjectFileName"); SolutionParserUtility::m_ideMacros.push_back("ProjectExt"); SolutionParserUtility::m_ideMacros.push_back("SolutionDir"); SolutionParserUtility::m_ideMacros.push_back("SolutionPath"); SolutionParserUtility::m_ideMacros.push_back("SolutionName"); SolutionParserUtility::m_ideMacros.push_back("SolutionExt"); SolutionParserUtility::m_ideMacros.push_back("TargetDir"); SolutionParserUtility::m_ideMacros.push_back("TargetPath"); SolutionParserUtility::m_ideMacros.push_back("TargetName"); SolutionParserUtility::m_ideMacros.push_back("TargetFileName"); SolutionParserUtility::m_ideMacros.push_back("VSInstallDir"); SolutionParserUtility::m_ideMacros.push_back("VCInstallDir"); SolutionParserUtility::m_ideMacros.push_back("FrameworkDir"); SolutionParserUtility::m_ideMacros.push_back("FrameworkVersion"); SolutionParserUtility::m_ideMacros.push_back("FrameworkSDKDir"); SolutionParserUtility::m_ideMacros.push_back("WebDeployPath"); SolutionParserUtility::m_ideMacros.push_back("WebDeployRoot"); SolutionParserUtility::m_ideMacros.push_back("SafeParentName"); SolutionParserUtility::m_ideMacros.push_back("SafeInputName"); SolutionParserUtility::m_ideMacros.push_back("SafeRootNamespace"); SolutionParserUtility::m_ideMacros.push_back("FxCopDir"); } SolutionParserVisualStudio::~SolutionParserVisualStudio() { } std::string SolutionParserVisualStudio::getToolID() const { return "vs"; } std::string SolutionParserVisualStudio::getSolutionName() { std::string result = ""; if (m_solutionName.size() > 0) { size_t pos = m_solutionName.find(".sln"); if (pos == std::string::npos) { pos = m_solutionName.find(".vcxproj"); } if (pos != std::string::npos) { result = m_solutionName.substr(0, pos); } else { result = m_solution; } } return result; } std::vector SolutionParserVisualStudio::getProjects() { std::vector projectFiles; if (m_solutionName.size() > 0 && m_solutionName.find(".vcxproj") != std::string::npos) { projectFiles.push_back(m_solutionName); return projectFiles; } if (m_solution.size() > 0) { std::vector blocks = getProjectBlocks(m_solution); for (unsigned int i = 0; i < blocks.size(); i++) { projectFiles.push_back(getProjectFilePath(blocks[i])); } } return projectFiles; } std::vector SolutionParserVisualStudio::getProjectFiles() { std::vector projectFiles; std::vector projectFilesNames = getProjects(); for (unsigned int i = 0; i < projectFilesNames.size(); i++) { projectFiles.push_back(loadFile(m_solutionPath + projectFilesNames[i])); } //projectFiles.push_back(loadFile(m_solutionPath + "foo.xml")); return projectFiles; } std::vector SolutionParserVisualStudio::getProjectItems() { std::vector projectItems = findProjectItems(); projectItems = SolutionParserUtility::makePathsCanonical(projectItems); return projectItems; } std::vector SolutionParserVisualStudio::getIncludePaths() { std::vector includePaths = findIncludePaths(); includePaths = SolutionParserUtility::makePathsCanonical(includePaths); return includePaths; } std::vector SolutionParserVisualStudio::getCompileFlags() { std::vector compilerFlags; std::vector projectFiles = getProjectFiles(); std::vector validExtensions; validExtensions.push_back(".c"); validExtensions.push_back(".cpp"); validExtensions.push_back(".h"); validExtensions.push_back(".hpp"); for (unsigned int i = 0; i < projectFiles.size(); i++) { TiXmlDocument doc; doc.Parse(projectFiles[i].c_str(), 0, TIXML_ENCODING_UTF8); std::string error = doc.ErrorDesc(); if (error.length() > 0) { LOG_ERROR_STREAM(<< "Failed to parse project file " << i << ": " << error); continue; } std::vector nodes = SolutionParserUtility::getAllTagsByName(doc.RootElement(), "PreprocessorDefinitions"); for (unsigned int j = 0; j < nodes.size(); j++) { compilerFlags.push_back(std::string(nodes[j]->GetText())); } } compilerFlags = seperateCompilerFlags(compilerFlags); for (unsigned int i = 0; i < compilerFlags.size(); i++) { compilerFlags[i] = "-D " + compilerFlags[i]; } for (unsigned int i = 0; i < m_compatibilityFlags.size(); i++) { compilerFlags.push_back(m_compatibilityFlags[i]); } std::set s(compilerFlags.begin(), compilerFlags.end()); compilerFlags.assign(s.begin(), s.end()); return compilerFlags; } ProjectSettings SolutionParserVisualStudio::getProjectSettings(const std::string& solutionFilePath) { LOG_INFO_STREAM(<< "Starting to parse VS solution"); LOG_INFO_STREAM(<< "Opening solution file"); openSolutionFile(solutionFilePath); SolutionParserUtility::m_ideMacroValues.clear(); SolutionParserUtility::m_ideMacroValues["SolutionPath"] = solutionFilePath; SolutionParserUtility::m_ideMacroValues["SolutionDir"] = m_solutionPath.substr(0, m_solutionPath.size() - 1); // remove trailing slash, would cause problems during path resolving SolutionParserUtility::m_ideMacroValues["SolutionName"] = getSolutionName(); SolutionParserUtility::m_ideMacroValues["SolutionFileName"] = m_solutionName; LOG_INFO_STREAM(<< "Setting meta information"); ProjectSettings settings; settings.setProjectName(getSolutionName()); settings.setProjectFileLocation(getSolutionPath()); settings.setVisualStudioSolutionPath(solutionFilePath); LOG_INFO_STREAM(<< "Parsing project files"); LOG_INFO_STREAM(<< "Setting source files"); std::vector sourceFiles = getProjectItems(); std::vector sourcePaths; for (const std::string& p : sourceFiles) { sourcePaths.push_back(FilePath(p)); } LOG_INFO_STREAM(<< "Parsing include files"); std::vector includePaths = getIncludePaths(); std::vector headerPaths; for (const std::string& p : includePaths) { headerPaths.push_back(FilePath(p)); } LOG_INFO_STREAM(<< "Setting compile flags"); std::vector compilerFlags = getCompileFlags(); settings.setCompilerFlags(compilerFlags); settings.setSourcePaths(sourcePaths); settings.setHeaderSearchPaths(headerPaths); LOG_INFO_STREAM(<< "Done parsing VS solution"); return settings; } std::string SolutionParserVisualStudio::getIdeName() const { return "Visual Studio"; } std::string SolutionParserVisualStudio::getButtonText() const { return "from Visual\nStudio Solution"; } std::string SolutionParserVisualStudio::getDescription() const { return "Create a new project from an existing Visual Studio Solution file."; } std::string SolutionParserVisualStudio::getIconPath() const { return "icon/project_vs_256_256.png"; } std::string SolutionParserVisualStudio::getFileExtension() const { return ".sln"; } std::vector SolutionParserVisualStudio::getProjectItemsNonCanonical() { return findProjectItems(); } std::vector SolutionParserVisualStudio::getIncludePathsNonCanonical() { return findIncludePaths(); } std::vector SolutionParserVisualStudio::findProjectItems() { std::vector projectItems; std::vector projectFilesNames = getProjects(); std::vector projectFiles = getProjectFiles(); std::vector relativeProjectPaths; std::vector validFileExtensions; validFileExtensions.push_back(".c"); validFileExtensions.push_back(".cpp"); validFileExtensions.push_back(".h"); validFileExtensions.push_back(".hpp"); for (unsigned int i = 0; i < projectFilesNames.size(); i++) { size_t pos = projectFilesNames[i].find_last_of("/"); if (pos == std::string::npos) { pos = projectFilesNames[i].find_last_of("\\"); } if (pos != std::string::npos) { relativeProjectPaths.push_back(projectFilesNames[i].substr(0, pos)); } else { relativeProjectPaths.push_back(""); } } for (unsigned int i = 0; i < projectFiles.size(); i++) { TiXmlDocument doc; doc.Parse(projectFiles[i].c_str(), 0, TIXML_ENCODING_UTF8); std::string error = doc.ErrorDesc(); if (error.length() > 0) { LOG_ERROR_STREAM(<< "Failed to parse project file " << i << ": " << error); continue; } TiXmlElement* root = SolutionParserUtility::getFirstTagByNameWithAttribute(doc.RootElement(), "ClCompile", "Include"); TiXmlElement* headerRoot = SolutionParserUtility::getFirstTagByNameWithAttribute(doc.RootElement(), "ClInclude", "Include"); if (root != NULL) { // std::string text(root->GetText()); std::string tag = root->Value(); std::string text = ""; if (root->Attribute("Include") != NULL) { text = root->Attribute("Include"); } if (text.find(".cpp") == std::string::npos && text.find(".c") == std::string::npos) { root = NULL; } else { root = root->Parent()->ToElement(); } } if (headerRoot != NULL) { std::string tag = headerRoot->Value(); std::string text = ""; if (headerRoot->Attribute("Include") != NULL) { text = headerRoot->Attribute("Include"); } if (text.find(".h") == std::string::npos && text.find(".hpp") == std::string::npos) { headerRoot = NULL; } else { headerRoot = headerRoot->Parent()->ToElement(); } } /*if (root == NULL) { TiXmlElement* root = getFirstTagByNameWithAttribute(doc.RootElement(), "ClInclude", "Include"); if (root != NULL) { root = root->Parent()->ToElement(); } else { root = NULL; } }*/ if (root != NULL) { for (TiXmlElement* child = root->FirstChildElement(); child != NULL; child = child->NextSiblingElement()) { std::string filePath = child->Attribute("Include"); if (boost::filesystem::exists(filePath) && SolutionParserUtility::checkValidFileExtension(filePath, validFileExtensions)) { projectItems.push_back(filePath); continue; } if (relativeProjectPaths[i].size() > 0) { filePath = relativeProjectPaths[i] + "/" + filePath; } if (SolutionParserUtility::checkValidFileExtension(filePath, validFileExtensions)) { projectItems.push_back(filePath); } } } if (headerRoot != NULL) { for (TiXmlElement* child = headerRoot->FirstChildElement(); child != NULL; child = child->NextSiblingElement()) { std::string filePath = child->Attribute("Include"); if (boost::filesystem::exists(filePath) && SolutionParserUtility::checkValidFileExtension(filePath, validFileExtensions)) { projectItems.push_back(filePath); continue; } if (relativeProjectPaths[i].size() > 0) { filePath = relativeProjectPaths[i] + "/" + filePath; } if (SolutionParserUtility::checkValidFileExtension(filePath, validFileExtensions)) { projectItems.push_back(filePath); } } } } std::set s(projectItems.begin(), projectItems.end()); projectItems.assign(s.begin(), s.end()); LOG_INFO_STREAM(<< "Found " << projectItems.size() << " code files"); projectItems = SolutionParserUtility::resolveEnvironmentVariables(projectItems); projectItems = makePathsAbsolute(projectItems); return projectItems; } std::vector SolutionParserVisualStudio::findIncludePaths() { std::vector includePaths; std::vector projectFiles = getProjectFiles(); std::vector validExtensions; validExtensions.push_back(".c"); validExtensions.push_back(".cpp"); validExtensions.push_back(".h"); validExtensions.push_back(".hpp"); for (unsigned int i = 0; i < projectFiles.size(); i++) { TiXmlDocument doc; doc.Parse(projectFiles[i].c_str(), 0, TIXML_ENCODING_UTF8); std::string error = doc.ErrorDesc(); if (error.length() > 0) { LOG_ERROR_STREAM(<< "Failed to parse project file " << i << ": " << error); continue; } std::vector nodes = SolutionParserUtility::getAllTagsByName(doc.RootElement(), "AdditionalIncludeDirectories"); for (unsigned int j = 0; j < nodes.size(); j++) { std::string path = nodes[j]->GetText(); includePaths.push_back(path); } } includePaths = seperateIncludePaths(includePaths); std::set s(includePaths.begin(), includePaths.end()); includePaths.assign(s.begin(), s.end()); LOG_INFO_STREAM(<< "Found " << includePaths.size() << " additional include paths"); includePaths = SolutionParserUtility::resolveEnvironmentVariables(includePaths); includePaths = makePathsAbsolute(includePaths); return includePaths; } std::vector SolutionParserVisualStudio::getProjectBlocks(const std::string& solution) const { std::vector blocks; std::string subSolution = solution; std::string solutionOpenTag = "\nProject"; std::string solutionCloseTag = "\nEndProject"; size_t blockStart = subSolution.find(solutionOpenTag, 0); size_t blockEnd = subSolution.find(solutionCloseTag, 0); while (blockStart != std::string::npos && blockEnd != std::string::npos) { std::string block = subSolution.substr(blockStart, blockEnd - blockStart + solutionCloseTag.size()); if (subSolution.size() > blockEnd + solutionCloseTag.size()) { subSolution = subSolution.substr(blockEnd + solutionCloseTag.size()); } else { subSolution = ""; } blockStart = subSolution.find(solutionOpenTag); blockEnd = subSolution.find(solutionCloseTag); blocks.push_back(block); } return blocks; } std::string SolutionParserVisualStudio::getProjectFilePath(const std::string& projectBlock) const { std::string projectFilePath; std::string fileEnding = "vcxproj"; size_t pos = projectBlock.find(fileEnding); if (pos != std::string::npos) { std::string searchArea = projectBlock.substr(0, pos + fileEnding.size()); pos = searchArea.find_last_of("\""); projectFilePath = searchArea.substr(pos + 1); } return projectFilePath; } std::vector SolutionParserVisualStudio::seperateIncludePaths(const std::vector& includePaths) const { std::vector seperatedPaths; for (unsigned int i = 0; i < includePaths.size(); i++) { std::vector paths = seperateIncludePaths(includePaths[i]); for (unsigned int j = 0; j < paths.size(); j++) { seperatedPaths.push_back(paths[j]); } } return seperatedPaths; } std::vector SolutionParserVisualStudio::seperateIncludePaths(const std::string& includePaths) const { std::string paths = includePaths; std::string seperator = ";"; std::vector seperatedPaths; size_t pos = includePaths.find(seperator); while (pos != std::string::npos) { std::string path = paths.substr(0, pos); paths = paths.substr(pos + 1); seperatedPaths.push_back(path); pos = paths.find(seperator); } if (paths.size() > 0) { seperatedPaths.push_back(paths); } return seperatedPaths; } std::vector SolutionParserVisualStudio::seperateCompilerFlags(const std::vector& compilerFlags) const { std::vector seperatedFlags; for (unsigned int i = 0; i < compilerFlags.size(); i++) { std::vector paths = seperateCompilerFlags(compilerFlags[i]); for (unsigned int j = 0; j < paths.size(); j++) { seperatedFlags.push_back(paths[j]); } } return seperatedFlags; } std::vector SolutionParserVisualStudio::seperateCompilerFlags(const std::string& compilerFlags) const { std::string flags = compilerFlags; std::string seperator = ";"; std::vector seperatedFlags; size_t pos = flags.find(seperator); while (pos != std::string::npos) { std::string flag = flags.substr(0, pos); flags = flags.substr(pos + 1); seperatedFlags.push_back(flag); pos = flags.find(seperator); } return seperatedFlags; }