diff --git a/src/cmake/CMakeListsWriter.cpp b/src/cmake/CMakeListsWriter.cpp index f552400..5aa1473 100644 --- a/src/cmake/CMakeListsWriter.cpp +++ b/src/cmake/CMakeListsWriter.cpp @@ -7,7 +7,7 @@ using namespace proj2cmake; void cmake::ListsWriter::operator()(std::ostream& os) { os << "SET(" << cmake::tokenize(mProject.first.name) << "_SRC" << std::endl; - for(auto&& f : mProject.second.compileFiles) + for(auto&& f : *mProject.second.spCompileFiles) { os << " " << f << std::endl; } @@ -16,9 +16,23 @@ void cmake::ListsWriter::operator()(std::ostream& os) os << std::endl; os << "SET(" << cmake::tokenize(mProject.first.name) << "_DEPS" << std::endl; - for(auto&& proc : mProject.second.referencedProjects) + for(auto&& proc : *mProject.second.spReferencedProjects) { os << " " << cmake::tokenize(proc.name) << std::endl; } os << " )" << std::endl; + + os << "SET(" << cmake::tokenize(mProject.first.name) << "_ADDITIONAL_INCLUDE_DIRS" << std::endl; + for (auto&& dir : *mProject.second.spAdditionalIncludeDirs) + { + os << " " << dir << std::endl; + } + os << " )" << std::endl; + + os << "SET(" << cmake::tokenize(mProject.first.name) << "_ADDITIONAL_LINK_DIRS" << std::endl; + for (auto&& dir : *mProject.second.spAdditionalLinkDirs) + { + os << " " << dir << std::endl; + } + os << " )" << std::endl; } diff --git a/src/cmake/CMakeMsc.cpp b/src/cmake/CMakeMsc.cpp index df3517d..a11ee79 100644 --- a/src/cmake/CMakeMsc.cpp +++ b/src/cmake/CMakeMsc.cpp @@ -1,4 +1,7 @@ #include "CMakeMsc.hpp" +#include "CMakeListsWriter.hpp" +#include "CMakeConfigTemplateWriter.hpp" +#include "CMakeSubDirRegistering.hpp" using namespace proj2cmake; @@ -61,3 +64,143 @@ std::string cmake::tokenize(const std::string& name) return res; } + + +void cmake::writeGeneratedNote(std::ostream& os) +{ + os << "#" << std::endl; + os << "# This file was genared by proj2cmake and will be overwritten on it's next run!" << std::endl; + os << "# Please put all configurations in the cmake_conf/*.cmake files." << std::endl; + os << "#" << std::endl; + os << std::endl; +} + +void cmake::writeProject(std::ofstream& os, const vcx::Solution& solution, const vcx::ProjectsPaths& dirToProj) +{ + os << "PROJECT(" << solution.name << ")" << std::endl; + os << std::endl; + os << "set(CMAKE_INCLUDE_CURRENT_DIR ON)" << std::endl; + os << std::endl; + fs::path confFile = "cmake_conf"; + confFile /= (solution.name + ".cmake"); + + //os << "IF(EXISTS \"${" << solution.name << "_SOURCE_DIR}/cmake_conf/" << solution.name << ".cmake\")" << std::endl; + os << "INCLUDE(\"${" << solution.name << "_SOURCE_DIR}/cmake_conf/" << solution.name << ".cmake\")" << std::endl; + //os << "ENDIF()" << std::endl; + os << std::endl; + + auto fullConfPath = solution.basePath / confFile; + if (fs::exists(fullConfPath) == false) + { + fs::create_directories(fullConfPath.parent_path()); + cmake::ConfigTemplateWriter writer(solution); + std::ofstream os(fullConfPath.native()); + writer(os); + } + + cmake::CMakeSubDirRegistering subDirRegister(os); + for (auto&& subDir : dirToProj) + { + if (solution.basePath == subDir.first) + continue; + + subDirRegister(subDir.second[0]->first.projectFile.parent_path()); + } + os << std::endl; +} + +void cmake::writeSolution(const vcx::Solution& solution) +{ + + vcx::ProjectsPaths dirToProj; + + for (auto p : solution.projects) + { + auto&& pInfo = p.first; + auto&& project = p.second; + + if (project.spCompileFiles->empty()) + continue; + + auto cmakeSrcFile = solution.basePath / pInfo.projectFile; + cmakeSrcFile.replace_extension(".cmake"); + + cmake::ListsWriter writer(p); + + std::ofstream os(cmakeSrcFile.native()); + cmake::writeGeneratedNote(os); + writer(os); + dirToProj[cmakeSrcFile.parent_path()].push_back(std::make_shared(p)); + } + + bool hasProject = false; + + for (auto p : dirToProj) + { + auto f = p.first / "CMakeLists.txt"; + std::ofstream os(f.native()); + cmake::writeGeneratedNote(os); + + os << "cmake_minimum_required(VERSION 2.8)" << std::endl; + os << std::endl; + if (p.first == solution.basePath) + { + cmake::writeProject(os, solution, dirToProj); + hasProject = true; + } + + for (auto&& pr : p.second) + { + auto&& pInfo = pr->first; + auto&& project = pr->second; + + os << std::endl; + + auto cmakeSrcFile = solution.basePath / pInfo.projectFile; + cmakeSrcFile.replace_extension(".cmake"); + std::cout << "Generating cmake " << cmakeSrcFile.string() << std::endl; + + os << "INCLUDE(\"" + cmakeSrcFile.filename().string() + "\")" << std::endl; + os << std::endl; + if (project.bMultiThreaded) + os << "set(CMAKE_CXX_FLAGS_RELEASE \"/MT\")" << std::endl; + if (project.bMultiThreadedDebug) + os << "set(CMAKE_CXX_FLAGS_DEBUG \"/MTd\")" << std::endl; + os << std::endl; + + os << cmake::cmakeStartType(pInfo.name, project.type) << std::endl; + os << " ${" << cmake::tokenize(pInfo.name) << "_SRC})" << std::endl; + os << std::endl; + + if (project.spAdditionalIncludeDirs->size() > 0) + { + os << "TARGET_INCLUDE_DIRECTORIES(" << cmake::tokenize(pInfo.name) << " PUBLIC " << std::endl; + os << " ${" << cmake::tokenize(pInfo.name) << "_ADDITIONAL_INCLUDE_DIRS})" << std::endl; + os << std::endl; + } + + if (project.spAdditionalLinkDirs->size() > 0) + { + os << "LINK_DIRECTORIES(" << cmake::tokenize(pInfo.name) << std::endl; + os << " ${" << cmake::tokenize(pInfo.name) << "_ADDITIONAL_LINK_DIRS})" << std::endl; + os << std::endl; + } + + os << "TARGET_LINK_LIBRARIES(" << cmake::tokenize(pInfo.name) << std::endl; + os << " ${" << cmake::tokenize(pInfo.name) << "_DEPS}" << std::endl; + os << " ${" << cmake::tokenize(pInfo.name) << "_ADDITIONAL_DEPS}" << std::endl; + os << " ${SOLUTION_" << cmake::cmakeTypeCaption(project.type) << "_DEPS}" << std::endl; + os << " ${SOLUTION_GENERAL_DEPS})" << std::endl; + os << std::endl; + } + } + + if (!hasProject) + { + auto f = solution.basePath / "CMakeLists.txt"; + std::ofstream os(f.native()); + cmake::writeProject(os, solution, dirToProj); + } + + std::cout << dirToProj.size() << " projects have been converted" << std::endl; +} \ No newline at end of file diff --git a/src/cmake/CMakeMsc.hpp b/src/cmake/CMakeMsc.hpp index 2a7a1ef..9fca8b4 100644 --- a/src/cmake/CMakeMsc.hpp +++ b/src/cmake/CMakeMsc.hpp @@ -4,6 +4,8 @@ #include "../vcx/VCXStructs.hpp" +namespace fs = boost::filesystem; + namespace proj2cmake { namespace cmake @@ -15,5 +17,11 @@ std::string cmakeStartType(const std::string& name, vcx::ConfigurationType type) std::string tokenize(const std::string& name); +void writeGeneratedNote(std::ostream& os); + +void writeProject(std::ofstream& os, const vcx::Solution& solution, const vcx::ProjectsPaths& dirToProj); + +void writeSolution(const vcx::Solution& solution); + } } diff --git a/src/main.cpp b/src/main.cpp index 2b2a1dd..d062f7e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,57 +1,11 @@ #include "vcx/VCXParser.hpp" - -#include "cmake/CMakeListsWriter.hpp" -#include "cmake/CMakeConfigTemplateWriter.hpp" +#include "vcx/VCXStructs.hpp" #include "cmake/CMakeMsc.hpp" -#include "cmake/CMakeSubDirRegistering.hpp" - #include +#include using namespace proj2cmake; -namespace fs = boost::filesystem; -using ProjectsPaths = std::map>; -void writeGeneratedNote(std::ostream& os, const char* procName) -{ - os << "#" << std::endl; - os << "# This file was genared by " << procName << " and will be overwritten on it's next run!" << std::endl; - os << "# Please put all configurations in the cmake_conf/*.cmake files." << std::endl; - os << "#" << std::endl; - os << std::endl; -} - -void writeProject(std::ofstream& os, const vcx::Solution& solution, const ProjectsPaths& dirToProj) -{ - os << "PROJECT(" << solution.name << ")" << std::endl; - os << std::endl; - - fs::path confFile = "cmake_conf"; - confFile /= (solution.name + ".cmake"); - - //os << "IF(EXISTS \"${" << solution.name << "_SOURCE_DIR}/cmake_conf/" << solution.name << ".cmake\")" << std::endl; - os << "INCLUDE(\"${" << solution.name << "_SOURCE_DIR}/" + confFile.string() + "\")" << std::endl; - //os << "ENDIF()" << std::endl; - os << std::endl; - - auto fullConfPath = solution.basePath / confFile; - if(fs::exists(fullConfPath) == false) - { - fs::create_directories(fullConfPath.parent_path()); - cmake::ConfigTemplateWriter writer(solution); - std::ofstream os(fullConfPath.native()); - writer(os); - } - - cmake::CMakeSubDirRegistering subDirRegister(os); - for(auto&& subDir : dirToProj) - { - if (solution.basePath == subDir.first) - continue; - - subDirRegister(subDir.second[0]->first.projectFile.parent_path()); - } - os << std::endl; -} int main(int argc, char** argv) { @@ -74,74 +28,13 @@ int main(int argc, char** argv) } vcx::SolutionParser parser(solutionFile); - auto solution = parser(); - - ProjectsPaths dirToProj; - for(auto&& p : solution.projects) - { - auto&& pInfo = p.first; - auto&& project = p.second; - - if(project.compileFiles.empty()) - continue; - - auto cmakeSrcFile = solution.basePath / pInfo.projectFile; - cmakeSrcFile.replace_extension(".cmake"); - cmake::ListsWriter writer(p); - - std::ofstream os(cmakeSrcFile.native()); - writeGeneratedNote(os, procName); - writer(os); - - dirToProj[cmakeSrcFile.parent_path()].push_back(&p); - } - - bool hasProject = false; - - for(auto&& p : dirToProj) - { - auto f = p.first / "CMakeLists.txt"; - std::ofstream os(f.native()); - writeGeneratedNote(os, procName); - - os << "cmake_minimum_required(VERSION 2.8)" << std::endl; - os << std::endl; - if(p.first == solution.basePath) - { - writeProject(os, solution, dirToProj); - hasProject = true; - } - - for(auto&& pr : p.second) - { - auto&& pInfo = pr->first; - auto&& project = pr->second; - - os << std::endl; - - auto cmakeSrcFile = solution.basePath / pInfo.projectFile; - cmakeSrcFile.replace_extension(".cmake"); - - os << "INCLUDE(\"" + cmakeSrcFile.filename().string() + "\")" << std::endl; - os << std::endl; - os << cmake::cmakeStartType(pInfo.name, project.type) << std::endl; - os << " ${" << cmake::tokenize(pInfo.name) << "_SRC})" << std::endl; - os << std::endl; - os << "TARGET_LINK_LIBRARIES(" << cmake::tokenize(pInfo.name) << std::endl; - os << " ${" << cmake::tokenize(pInfo.name) << "_DEPS}" << std::endl; - os << " ${" << cmake::tokenize(pInfo.name) << "_ADDITIONAL_DEPS}" << std::endl; - os << " ${SOLUTION_" << cmake::cmakeTypeCaption(project.type) << "_DEPS}" << std::endl; - os << " ${SOLUTION_GENERAL_DEPS})" << std::endl; - os << std::endl; - } + try { + cmake::writeSolution(parser()); } - - if (!hasProject) - { - auto f = solution.basePath / "CMakeLists.txt"; - std::ofstream os(f.native()); - writeProject(os, solution, dirToProj); + catch (std::runtime_error e) { + std::cerr << e.what() << std::endl; + return 1; } return 0; diff --git a/src/vcx/VCXParser.cpp b/src/vcx/VCXParser.cpp index a0a8dc4..b0435c6 100644 --- a/src/vcx/VCXParser.cpp +++ b/src/vcx/VCXParser.cpp @@ -1,11 +1,11 @@ #include "VCXParser.hpp" #include - +#include using namespace proj2cmake::vcx; namespace { -inline std::string pathToNative(std::string&& in) +std::string pathToNative(std::string& in) { for(auto& c : in) if(c == '\\') @@ -13,13 +13,13 @@ inline std::string pathToNative(std::string&& in) return in; } -inline void toUpper(char& c) +void toUpper(char& c) { if(c >= 'a' && c <= 'z') c += 'A' - 'a'; } -inline std::string toUpper(std::string&& in) +std::string toUpper(std::string& in) { for(auto& c : in) toUpper(c); @@ -33,7 +33,7 @@ fs::path SolutionParser::basePath() const } SolutionParser::SolutionParser(fs::path slnFile) - : mSlnFile(std::move(slnFile)) + : mSlnFile(slnFile) {} Solution SolutionParser::operator()() @@ -44,16 +44,23 @@ Solution SolutionParser::operator()() res.name = mSlnFile.stem().string(); auto infos = parseSolution(mSlnFile); - for(auto&& projInfo : infos) - res.projects[projInfo] = parseProject(projInfo); + for(auto&& projInfo : infos) { + try { + res.projects[projInfo] = parseProject(projInfo); + } + catch (std::exception e) { + std::cerr << "ERROR: " << e.what() << std::endl; + } + } + for(auto&& proj : res.projects) { - for(auto&& refProj : proj.second.referencedProjects) + for(auto&& refProj : *proj.second.spReferencedProjects) { auto itr = res.projects.find(refProj); if(itr == res.projects.end()) - std::cerr << "Warning: Project " << refProj.guid << " is referenced but can not be found" << std::endl; + std::cerr << "WARNING: Project " << refProj.guid << " is referenced but can not be found" << std::endl; else refProj.name = itr->first.name; } @@ -74,64 +81,126 @@ Project SolutionParser::parseProject(const ProjectInfo& projInfo) std::cout << "Parsing file: " << projFilePath << std::endl; if(fs::exists(projFilePath) == false) throw std::runtime_error("Project file '"+ projFilePath.string() + "' not found!"); - boost::property_tree::xml_parser::read_xml(projFilePath.native(), pt); + boost::property_tree::xml_parser::read_xml(projFilePath.string(), pt, 0, std::locale()); std::string type; auto project = pt.get_child("Project"); for(auto&& pp : project) { + if(type.empty() && pp.first == "PropertyGroup") { type = pp.second.get("ConfigurationType", ""); continue; } - else if(pp.first != "ItemGroup") - continue; - for(auto&& ip : pp.second) + if (pp.first == "ItemDefinitionGroup") + { + for (auto&& ip : pp.second) + { + if (ip.first == "ClCompile") + { + for (auto&& jp : ip.second) + { + if (jp.first == "AdditionalIncludeDirectories") + { + auto sAddtionalIncludeDirs = jp.second.data(); + auto i = 0; + auto pos = sAddtionalIncludeDirs.find(';'); + while (pos != std::string::npos) + { + auto dir = sAddtionalIncludeDirs.substr(i, pos - i); + i = ++pos; + pos = sAddtionalIncludeDirs.find(';', pos); + dir = pathToNative(dir); + if (std::find(res.spAdditionalIncludeDirs->begin(), + res.spAdditionalIncludeDirs->end(), dir) == + res.spAdditionalIncludeDirs->end()) + res.spAdditionalIncludeDirs->push_back(dir); + } + } + else if (jp.first == "RuntimeLibrary") + { + auto sRuntimeLibrary = jp.second.data(); + if (sRuntimeLibrary == "MultiThreaded") + res.bMultiThreaded = true; + else if (sRuntimeLibrary == "MultiThreadedDebug") + res.bMultiThreadedDebug = true; + } + } + } + else if (ip.first == "Link") + { + for (auto&& jp : ip.second) + { + if (jp.first == "AdditionalLibraryDirectories") + { + auto sAddtionalLinkDirs = jp.second.data(); + auto i = 0; + auto pos = sAddtionalLinkDirs.find(';'); + while (pos != std::string::npos) + { + auto dir = sAddtionalLinkDirs.substr(i, pos - i); + i = ++pos; + pos = sAddtionalLinkDirs.find(';', pos); + dir = pathToNative(dir); + if (std::find(res.spAdditionalLinkDirs->begin(), + res.spAdditionalLinkDirs->end(), dir) == + res.spAdditionalLinkDirs->end()) + res.spAdditionalLinkDirs->push_back(dir); + } + } + + } + } + } + } + + else if(pp.first == "ItemGroup") { - if(ip.first == "ClCompile") - { - auto f = ip.second.get(".Include"); - f = pathToNative(std::move(f)); - res.compileFiles.push_back(f); - } - else if(ip.first == "ClInclude") - { - auto f = ip.second.get(".Include"); - f = pathToNative(std::move(f)); - res.includeFiles.push_back(f); - } - else if(ip.first == "ProjectReference") - { - ProjectInfo pInfo; - auto f = ip.second.get(".Include"); - pInfo.projectFile = pathToNative(std::move(f)); - pInfo.guid = toUpper(ip.second.get("Project")); - res.referencedProjects.push_back(pInfo); - } + for (auto&& ip : pp.second) + { + if (ip.first == "ClCompile") + { + auto f = ip.second.get(".Include"); + res.spCompileFiles->push_back(pathToNative(f)); + } + else if (ip.first == "ClInclude") + { + auto f = ip.second.get(".Include"); + res.spIncludeFiles->push_back(pathToNative(f)); + } + else if (ip.first == "ProjectReference") + { + ProjectInfo pInfo; + auto f = ip.second.get(".Include"); + pInfo.projectFile = pathToNative(f); + pInfo.guid = toUpper(ip.second.get("Project")); + res.spReferencedProjects->push_back(pInfo); + } + } } } - if(type == "Application") - res.type = ConfigurationType::Application; - else if(type == "DynamicLibrary") - res.type = ConfigurationType::DynamicLibrary; - else if(type == "StaticLibrary") - res.type = ConfigurationType::StaticLibrary; - else if(type == "Utility") - res.type = ConfigurationType::Utility; - else if(type == "Makefile") - res.type = ConfigurationType::Makefile; + if (type == "Application") + res.type = ConfigurationType::Application; + else if (type == "DynamicLibrary") + res.type = ConfigurationType::DynamicLibrary; + else if (type == "StaticLibrary") + res.type = ConfigurationType::StaticLibrary; + else if (type == "Utility") + res.type = ConfigurationType::Utility; + else if (type == "Makefile") + res.type = ConfigurationType::Makefile; else throw std::runtime_error("Project '" + projInfo.name + "' has an invalid ConfigurationType ('" + type + "')"); - std::cout << " Type: " << type << ", CompileFiles: " << res.compileFiles.size() << ", IncludeFiles: " << res.includeFiles.size() << ", ProjectReferences: " << res.referencedProjects.size() << std::endl; + std::cout << " Type: " << type << ", CompileFiles: " << res.spCompileFiles->size() << ", IncludeFiles: " << res.spIncludeFiles->size() << ", ProjectReferences: " << res.spReferencedProjects->size() << std::endl; - std::sort(begin(res.compileFiles), end(res.compileFiles)); - std::sort(begin(res.includeFiles), end(res.includeFiles)); - std::sort(begin(res.referencedProjects), end(res.referencedProjects)); + std::sort(begin(*res.spCompileFiles), end(*res.spCompileFiles)); + std::sort(begin(*res.spIncludeFiles), end(*res.spIncludeFiles)); + std::sort(begin(*res.spReferencedProjects), end(*res.spReferencedProjects)); return res; } @@ -179,6 +248,8 @@ std::vector SolutionParser::parseSolution(std::istream& is) std::vector SolutionParser::parseSolution(const fs::path& solutionFile) { - std::fstream is(solutionFile.native()); + std::fstream is(solutionFile.native(), std::fstream::in); + if (!is.is_open()) + throw std::runtime_error("Error opening file: " + std::string(strerror(errno))); return parseSolution(is); } diff --git a/src/vcx/VCXStructs.hpp b/src/vcx/VCXStructs.hpp index 102e2ea..9459e68 100644 --- a/src/vcx/VCXStructs.hpp +++ b/src/vcx/VCXStructs.hpp @@ -4,7 +4,7 @@ #include #include #include - +#include #include #include #include @@ -31,6 +31,9 @@ struct ProjectInfo std::string guid; std::string name; fs::path projectFile; + ProjectInfo() = default; + + ProjectInfo(const ProjectInfo&) = default; bool operator<(const ProjectInfo& other) const { @@ -45,14 +48,26 @@ struct ProjectInfo struct Project { + Project() { + bMultiThreaded = false; + bMultiThreadedDebug = false; + spCompileFiles = std::make_shared>(); + spIncludeFiles = std::make_shared>(); + spAdditionalIncludeDirs = std::make_shared>(); + spAdditionalLinkDirs = std::make_shared>(); + spReferencedProjects = std::make_shared>(); + } ConfigurationType type; - std::vector compileFiles; - std::vector includeFiles; - std::vector referencedProjects; + bool bMultiThreaded; + bool bMultiThreadedDebug; + std::shared_ptr> spCompileFiles; + std::shared_ptr> spIncludeFiles; + std::shared_ptr> spAdditionalIncludeDirs; + std::shared_ptr> spAdditionalLinkDirs; + std::shared_ptr> spReferencedProjects; }; -using ProjectPair = std::pair; struct Solution { @@ -61,5 +76,7 @@ struct Solution std::map projects; }; -} +using ProjectPair = std::pair; +using ProjectsPaths = std::map>>; +}; }