From 0c4e8c70a0449bde505d696d120ac0ef207f92a5 Mon Sep 17 00:00:00 2001 From: Eran Ifrah Date: Fri, 10 Jan 2014 09:20:11 +0200 Subject: [PATCH] Add support for codelite IDE project fles --- Help/generator/CodeLite.rst | 24 ++ Help/manual/cmake-generators.7.rst | 1 + Source/CMakeLists.txt | 2 + Source/cmExtraCodeLiteGenerator.cxx | 487 ++++++++++++++++++++++++++++ Source/cmExtraCodeLiteGenerator.h | 54 +++ Source/cmake.cxx | 3 + 6 files changed, 571 insertions(+) create mode 100644 Help/generator/CodeLite.rst create mode 100644 Source/cmExtraCodeLiteGenerator.cxx create mode 100644 Source/cmExtraCodeLiteGenerator.h diff --git a/Help/generator/CodeLite.rst b/Help/generator/CodeLite.rst new file mode 100644 index 000000000..dbc46d72b --- /dev/null +++ b/Help/generator/CodeLite.rst @@ -0,0 +1,24 @@ +CodeLite +---------- + +Generates CodeLite project files. + +Project files for CodeLite will be created in the top directory and +in every subdirectory which features a CMakeLists.txt file containing +a PROJECT() call. The appropriate make program can build the +project through the default make target. A "make install" target is +also provided. + +This "extra" generator may be specified as: + +``CodeLite - MinGW Makefiles`` + Generate with :generator:`MinGW Makefiles`. + +``CodeLite - NMake Makefiles`` + Generate with :generator:`NMake Makefiles`. + +``CodeLite - Ninja`` + Generate with :generator:`Ninja`. + +``CodeLite - Unix Makefiles`` + Generate with :generator:`Unix Makefiles`. diff --git a/Help/manual/cmake-generators.7.rst b/Help/manual/cmake-generators.7.rst index b72c7efa9..8d0c70418 100644 --- a/Help/manual/cmake-generators.7.rst +++ b/Help/manual/cmake-generators.7.rst @@ -79,6 +79,7 @@ The following extra generators are known to CMake. :maxdepth: 1 /generator/CodeBlocks + /generator/CodeLite /generator/Eclipse CDT4 /generator/KDevelop3 /generator/Kate diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 3c944fbba..dbb922d70 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -186,6 +186,8 @@ set(SRCS cmExportSetMap.cxx cmExtraCodeBlocksGenerator.cxx cmExtraCodeBlocksGenerator.h + cmExtraCodeLiteGenerator.cxx + cmExtraCodeLiteGenerator.h cmExtraEclipseCDT4Generator.cxx cmExtraEclipseCDT4Generator.h cmExtraKateGenerator.cxx diff --git a/Source/cmExtraCodeLiteGenerator.cxx b/Source/cmExtraCodeLiteGenerator.cxx new file mode 100644 index 000000000..b156691e5 --- /dev/null +++ b/Source/cmExtraCodeLiteGenerator.cxx @@ -0,0 +1,487 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2004-2009 Kitware, Inc. + Copyright 2004 Alexander Neundorf (neundorf@kde.org) + Copyright 2013 Eran Ifrah (eran.ifrah@gmail.com) + + 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 "cmExtraCodeLiteGenerator.h" +#include "cmGlobalUnixMakefileGenerator3.h" +#include "cmLocalUnixMakefileGenerator3.h" +#include "cmMakefile.h" +#include "cmake.h" +#include "cmSourceFile.h" +#include "cmGeneratedFileStream.h" +#include "cmSystemTools.h" + +#include +#include +#include +#include "cmXMLSafe.h" +#include + +//---------------------------------------------------------------------------- +void cmExtraCodeLiteGenerator::GetDocumentation(cmDocumentationEntry& entry, + const char*) const +{ + entry.Name = this->GetName(); + entry.Brief = "Generates CodeLite project files."; +} + +cmExtraCodeLiteGenerator::cmExtraCodeLiteGenerator() + : cmExternalMakefileProjectGenerator() + , ConfigName("NoConfig") + , CpuCount(2) +{ +#if defined(_WIN32) + this->SupportedGlobalGenerators.push_back("MinGW Makefiles"); + this->SupportedGlobalGenerators.push_back("NMake Makefiles"); +#endif + this->SupportedGlobalGenerators.push_back("Ninja"); + this->SupportedGlobalGenerators.push_back("Unix Makefiles"); +} + +void cmExtraCodeLiteGenerator::Generate() +{ + // Hold root tree information for creating the workspace + std::string workspaceProjectName; + std::string workspaceOutputDir; + std::string workspaceFileName; + std::string workspaceSourcePath; + std::string lprjdebug; + + cmGeneratedFileStream fout; + + // loop projects and locate the root project. + // and extract the information for creating the worspace + for (std::map >::const_iterator + it = this->GlobalGenerator->GetProjectMap().begin(); + it!= this->GlobalGenerator->GetProjectMap().end(); + ++it) + { + const cmMakefile* mf =it->second[0]->GetMakefile(); + this->ConfigName = GetConfigurationName( mf ); + + if (strcmp(mf->GetStartOutputDirectory(), + mf->GetHomeOutputDirectory()) == 0) + { + workspaceOutputDir = mf->GetStartOutputDirectory(); + workspaceProjectName = mf->GetProjectName(); + workspaceSourcePath = mf->GetHomeDirectory(); + workspaceFileName = workspaceOutputDir+"/"; + workspaceFileName += workspaceProjectName + ".workspace"; + this->WorkspacePath = mf->GetStartOutputDirectory();; + + fout.Open(workspaceFileName.c_str(), false, false); + fout << "\n" + "\n"; + } + } + + // for each sub project in the workspace create a codelite project + for (std::map >::const_iterator + it = this->GlobalGenerator->GetProjectMap().begin(); + it!= this->GlobalGenerator->GetProjectMap().end(); + ++it) + { + // retrive project information + const cmMakefile* mf = it->second[0]->GetMakefile(); + std::string outputDir = mf->GetStartOutputDirectory(); + std::string projectName = mf->GetProjectName(); + std::string filename = outputDir + "/" + projectName + ".project"; + + // Make the project file relative to the workspace + filename = cmSystemTools::RelativePath(this->WorkspacePath.c_str(), + filename.c_str()); + + // create a project file + this->CreateProjectFile(it->second); + fout << " \n"; + lprjdebug += "ConfigName + "\"/>\n"; + } + + fout << " \n" + " ConfigName << "\" Selected=\"yes\">\n" + " " << lprjdebug << "" + " \n" + " \n" + "\n"; +} + +/* create the project file */ +void cmExtraCodeLiteGenerator::CreateProjectFile( + const std::vector& lgs) +{ + const cmMakefile* mf = lgs[0]->GetMakefile(); + std::string outputDir = mf->GetStartOutputDirectory(); + std::string projectName = mf->GetProjectName(); + std::string filename = outputDir + "/"; + + filename += projectName + ".project"; + this->CreateNewProjectFile(lgs, filename); +} + +void cmExtraCodeLiteGenerator +::CreateNewProjectFile(const std::vector& lgs, + const std::string& filename) +{ + const cmMakefile* mf=lgs[0]->GetMakefile(); + cmGeneratedFileStream fout(filename.c_str()); + if(!fout) + { + return; + } + + // figure out the compiler + //std::string compiler = this->GetCBCompilerId(mf); + std::string workspaceSourcePath = mf->GetHomeDirectory(); + std::string workspaceOutputDir = mf->GetHomeOutputDirectory(); + std::vector outputFiles = mf->GetOutputFiles(); + std::string projectName = mf->GetProjectName(); + std::string incDirs; + std::vector incDirsVec = + mf->GetIncludeDirectoriesEntries(); + std::vector::const_iterator iterInc = incDirsVec.begin(); + + //std::cout << "GetIncludeDirectories:" << std::endl; + for(; iterInc != incDirsVec.end(); ++iterInc ) + { + //std::cout << (*ItStrVec) << std::endl; + incDirs += iterInc->Value + " "; + } + + //////////////////////////////////// + fout << "\n" + "GetProjectName() + << "\" InternalType=\"\">\n"; + + // Collect all used source files in the project + // Sort them into two containers, one for C/C++ implementation files + // which may have an acompanying header, one for all other files + std::string projectType; + + std::map cFiles; + std::set otherFiles; + for (std::vector::const_iterator lg=lgs.begin(); + lg!=lgs.end(); lg++) + { + cmMakefile* makefile=(*lg)->GetMakefile(); + cmTargets& targets=makefile->GetTargets(); + for (cmTargets::iterator ti = targets.begin(); + ti != targets.end(); ti++) + { + + switch(ti->second.GetType()) + { + case cmTarget::EXECUTABLE: + { + projectType = "Executable"; + } + break; + case cmTarget::STATIC_LIBRARY: + { + projectType = "Static Library"; + } + break; + case cmTarget::SHARED_LIBRARY: + { + projectType = "Dynamic Library"; + } + break; + case cmTarget::MODULE_LIBRARY: + { + projectType = "Dynamic Library"; + } + break; + default: // intended fallthrough + break; + } + + switch(ti->second.GetType()) + { + case cmTarget::EXECUTABLE: + case cmTarget::STATIC_LIBRARY: + case cmTarget::SHARED_LIBRARY: + case cmTarget::MODULE_LIBRARY: + { + std::vector sources; + ti->second.GetSourceFiles(sources); + for (std::vector::const_iterator si=sources.begin(); + si!=sources.end(); si++) + { + // check whether it is a C/C++ implementation file + bool isCFile = false; + if ((*si)->GetLanguage() && (*(*si)->GetLanguage() == 'C')) + { + for(std::vector::const_iterator + ext = mf->GetSourceExtensions().begin(); + ext != mf->GetSourceExtensions().end(); + ++ext) + { + if ((*si)->GetExtension() == *ext) + { + isCFile = true; + break; + } + } + } + + // then put it accordingly into one of the two containers + if (isCFile) + { + cFiles[(*si)->GetFullPath()] = *si ; + } + else + { + otherFiles.insert((*si)->GetFullPath()); + } + } + } + default: // intended fallthrough + break; + } + } + } + + // The following loop tries to add header files matching to implementation + // files to the project. It does that by iterating over all source files, + // replacing the file name extension with ".h" and checks whether such a + // file exists. If it does, it is inserted into the map of files. + // A very similar version of that code exists also in the kdevelop + // project generator. + for (std::map::const_iterator + sit=cFiles.begin(); + sit!=cFiles.end(); + ++sit) + { + std::string headerBasename=cmSystemTools::GetFilenamePath(sit->first); + headerBasename+="/"; + headerBasename+=cmSystemTools::GetFilenameWithoutExtension(sit->first); + + // check if there's a matching header around + for(std::vector::const_iterator + ext = mf->GetHeaderExtensions().begin(); + ext != mf->GetHeaderExtensions().end(); + ++ext) + { + std::string hname=headerBasename; + hname += "."; + hname += *ext; + // if it's already in the set, don't check if it exists on disk + std::set::const_iterator headerIt=otherFiles.find(hname); + if (headerIt != otherFiles.end()) + { + break; + } + + if(cmSystemTools::FileExists(hname.c_str())) + { + otherFiles.insert(hname); + break; + } + } + } + + // Get the project path ( we need it later to convert files to + // their relative path) + std::string projectPath = cmSystemTools::GetFilenamePath(filename); + + // Create 2 virtual folders: src and include + // and place all the implementation files into the src + // folder, the rest goes to the include folder + fout<< " \n"; + + // insert all source files in the codelite project + // first the C/C++ implementation files, then all others + for (std::map::const_iterator + sit=cFiles.begin(); + sit!=cFiles.end(); + ++sit) + { + std::string relativePath = + cmSystemTools::RelativePath(projectPath.c_str(), sit->first.c_str()); + fout<< " \n"; + } + fout<< " \n"; + fout<< " \n"; + for (std::set::const_iterator + sit=otherFiles.begin(); + sit!=otherFiles.end(); + ++sit) + { + std::string relativePath = + cmSystemTools::RelativePath(projectPath.c_str(), sit->c_str()); + fout << " \n"; + } + fout << " \n"; + + // Get the number of CPUs. We use this information for the make -jN + // command + cmsys::SystemInformation info; + info.RunCPUCheck(); + + this->CpuCount = info.GetNumberOfLogicalCPU() * + info.GetNumberOfPhysicalCPU(); + + std::string cleanCommand = GetCleanCommand(mf); + std::string buildCommand = GetBuildCommand(mf); + std::string rebuildCommand = GetRebuildCommand(mf); + std::string singleFileCommand = GetSingleFileBuildCommand(mf); + + std::string codeliteCompilerName = this->GetCodeLiteCompilerName(mf); + + fout << "\n" + " \n" + " ConfigName << "\" CompilerType=\"" + << codeliteCompilerName << "\" DebuggerType=\"GNU gdb debugger\" " + "Type=\"" + << projectType << "\" BuildCmpWithGlobalSettings=\"append\" " + "BuildLnkWithGlobalSettings=\"append\" " + "BuildResWithGlobalSettings=\"append\">\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " " << rebuildCommand << "\n" + " " << cleanCommand << "\n" + " " << buildCommand << "\n" + " " << singleFileCommand + << "\n" + " \n" + " $(WorkspacePath)\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n"; +} + +std::string +cmExtraCodeLiteGenerator::GetCodeLiteCompilerName(const cmMakefile* mf) const +{ + // figure out which language to use + // for now care only for C and C++ + std::string compilerIdVar = "CMAKE_CXX_COMPILER_ID"; + if (this->GlobalGenerator->GetLanguageEnabled("CXX") == false) + { + compilerIdVar = "CMAKE_C_COMPILER_ID"; + } + + std::string compilerId = mf->GetSafeDefinition(compilerIdVar.c_str()); + std::string compiler = "gnu g++"; // default to g++ + + // Since we need the compiler for parsing purposes only + // it does not matter if we use clang or clang++, same as + // "gnu gcc" vs "gnu g++" + if (compilerId == "MSVC") + { + compiler = "VC++"; + } + else if (compilerId == "Clang") + { + compiler = "clang++"; + } + else if (compilerId == "GNU") + { + compiler = "gnu g++"; + } + return compiler; +} + +std::string +cmExtraCodeLiteGenerator::GetConfigurationName(const cmMakefile* mf) const +{ + std::string confName = mf->GetSafeDefinition("CMAKE_BUILD_TYPE"); + // Trim the configuration name from whitespaces (left and right) + confName.erase(0, confName.find_first_not_of(" \t\r\v\n")); + confName.erase(confName.find_last_not_of(" \t\r\v\n")+1); + if ( confName.empty() ) + { + confName = "NoConfig"; + } + return confName; +} + +std::string +cmExtraCodeLiteGenerator::GetBuildCommand(const cmMakefile* mf) const +{ + std::stringstream ss; + std::string generator = mf->GetSafeDefinition("CMAKE_GENERATOR"); + std::string make = mf->GetRequiredDefinition("CMAKE_MAKE_PROGRAM"); + std::string buildCommand = make; // Default + if ( generator == "NMake Makefiles" ) + { + buildCommand = make; + } + else if ( generator == "MinGW Makefiles" || + generator == "Unix Makefiles" || + generator == "Ninja" ) + { + ss << make; + buildCommand = ss.str(); + } + return buildCommand; +} + +std::string +cmExtraCodeLiteGenerator::GetCleanCommand(const cmMakefile* mf) const +{ + return GetBuildCommand(mf) + " clean"; +} + +std::string +cmExtraCodeLiteGenerator::GetRebuildCommand(const cmMakefile* mf) const +{ + return GetCleanCommand(mf) + cmXMLSafe(" && ").str() + GetBuildCommand(mf); +} + +std::string +cmExtraCodeLiteGenerator::GetSingleFileBuildCommand +(const cmMakefile* mf) const +{ + std::string buildCommand; + std::string make = mf->GetRequiredDefinition("CMAKE_MAKE_PROGRAM"); + std::string generator = mf->GetSafeDefinition("CMAKE_GENERATOR"); + if ( generator == "Unix Makefiles" || generator == "MinGW Makefiles" ) + { + std::stringstream ss; + ss << make << " -f$(ProjectPath)/Makefile $(CurrentFileName).cpp.o"; + buildCommand = ss.str(); + } + return buildCommand; +} diff --git a/Source/cmExtraCodeLiteGenerator.h b/Source/cmExtraCodeLiteGenerator.h new file mode 100644 index 000000000..984313e0b --- /dev/null +++ b/Source/cmExtraCodeLiteGenerator.h @@ -0,0 +1,54 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2004-2009 Kitware, Inc. + Copyright 2004 Alexander Neundorf (neundorf@kde.org) + Copyright 2013 Eran Ifrah (eran.ifrah@gmail.com) + + 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 cmGlobalCodeLiteGenerator_h +#define cmGlobalCodeLiteGenerator_h + +#include "cmExternalMakefileProjectGenerator.h" + +class cmLocalGenerator; + +class cmExtraCodeLiteGenerator : public cmExternalMakefileProjectGenerator +{ +protected: + std::string ConfigName; + std::string WorkspacePath; + unsigned int CpuCount; + +protected: + std::string GetCodeLiteCompilerName(const cmMakefile* mf) const; + std::string GetConfigurationName( const cmMakefile* mf ) const; + std::string GetBuildCommand(const cmMakefile* mf) const; + std::string GetCleanCommand(const cmMakefile* mf) const; + std::string GetRebuildCommand(const cmMakefile* mf) const; + std::string GetSingleFileBuildCommand(const cmMakefile* mf) const; +public: + cmExtraCodeLiteGenerator(); + + virtual const char* GetName() const + { return cmExtraCodeLiteGenerator::GetActualName();} + static const char* GetActualName() { return "CodeLite";} + static cmExternalMakefileProjectGenerator* New() + { return new cmExtraCodeLiteGenerator; } + /** Get the documentation entry for this generator. */ + virtual void GetDocumentation(cmDocumentationEntry& entry, + const char* fullName) const; + + virtual void Generate(); + void CreateProjectFile(const std::vector& lgs); + + void CreateNewProjectFile(const std::vector& lgs, + const std::string& filename); +}; + +#endif diff --git a/Source/cmake.cxx b/Source/cmake.cxx index 1dd8a664c..8e52f9661 100644 --- a/Source/cmake.cxx +++ b/Source/cmake.cxx @@ -69,6 +69,7 @@ #endif #include "cmGlobalUnixMakefileGenerator3.h" #include "cmGlobalNinjaGenerator.h" +#include "cmExtraCodeLiteGenerator.h" #if !defined(CMAKE_BOOT_MINGW) # include "cmExtraCodeBlocksGenerator.h" @@ -991,6 +992,8 @@ void cmake::AddDefaultExtraGenerators() this->AddExtraGenerator(cmExtraCodeBlocksGenerator::GetActualName(), &cmExtraCodeBlocksGenerator::New); + this->AddExtraGenerator(cmExtraCodeLiteGenerator::GetActualName(), + &cmExtraCodeLiteGenerator::New); this->AddExtraGenerator(cmExtraSublimeTextGenerator::GetActualName(), &cmExtraSublimeTextGenerator::New); this->AddExtraGenerator(cmExtraKateGenerator::GetActualName(),