/*============================================================================ 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 "cmStandardIncludes.h" #include "cmXMLSafe.h" //---------------------------------------------------------------------------- void cmExtraCodeLiteGenerator::GetDocumentation(cmDocumentationEntry& entry, const std::string&) 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->GetCurrentBinaryDirectory(), mf->GetHomeOutputDirectory()) == 0) { workspaceOutputDir = mf->GetCurrentBinaryDirectory(); workspaceProjectName = mf->GetProjectName(); workspaceSourcePath = mf->GetHomeDirectory(); workspaceFileName = workspaceOutputDir+"/"; workspaceFileName += workspaceProjectName + ".workspace"; this->WorkspacePath = mf->GetCurrentBinaryDirectory();; 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->GetCurrentBinaryDirectory(); 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->GetCurrentBinaryDirectory(); 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; } //////////////////////////////////// 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, makefile->GetSafeDefinition("CMAKE_BUILD_TYPE")); for (std::vector::const_iterator si=sources.begin(); si!=sources.end(); si++) { // check whether it is a C/C++ implementation file bool isCFile = false; std::string lang = (*si)->GetLanguage(); if (lang == "C" || lang == "CXX") { std::string srcext = (*si)->GetExtension(); for(std::vector::const_iterator ext = mf->GetSourceExtensions().begin(); ext != mf->GetSourceExtensions().end(); ++ext) { if (srcext == *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); 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::string generator = mf->GetSafeDefinition("CMAKE_GENERATOR"); std::string make = mf->GetRequiredDefinition("CMAKE_MAKE_PROGRAM"); std::string buildCommand = make; // Default if ( generator == "NMake Makefiles" || generator == "Ninja" ) { buildCommand = make; } else if ( generator == "MinGW Makefiles" || generator == "Unix Makefiles" ) { std::ostringstream ss; ss << make << " -j " << this->CpuCount; 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::ostringstream ss; ss << make << " -f$(ProjectPath)/Makefile $(CurrentFileName).cpp.o"; buildCommand = ss.str(); } return buildCommand; }