| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626 | /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying   file Copyright.txt or https://cmake.org/licensing for details.  */#include "cmExtraCodeLiteGenerator.h"#include "cmGeneratedFileStream.h"#include "cmGeneratorTarget.h"#include "cmGlobalGenerator.h"#include "cmLocalGenerator.h"#include "cmMakefile.h"#include "cmSourceFile.h"#include "cmState.h"#include "cmSystemTools.h"#include "cmXMLWriter.h"#include "cmake.h"#include <cmsys/SystemInformation.hxx>#include <map>#include <set>#include <sstream>#include <string.h>#include <utility>cmExtraCodeLiteGenerator::cmExtraCodeLiteGenerator()  : cmExternalMakefileProjectGenerator()  , ConfigName("NoConfig")  , CpuCount(2){}cmExternalMakefileProjectGeneratorFactory*cmExtraCodeLiteGenerator::GetFactory(){  static cmExternalMakefileProjectGeneratorSimpleFactory<    cmExtraCodeLiteGenerator>    factory("CodeLite", "Generates CodeLite project files.");  if (factory.GetSupportedGlobalGenerators().empty()) {#if defined(_WIN32)    factory.AddSupportedGlobalGenerator("MinGW Makefiles");    factory.AddSupportedGlobalGenerator("NMake Makefiles");#endif    factory.AddSupportedGlobalGenerator("Ninja");    factory.AddSupportedGlobalGenerator("Unix Makefiles");  }  return &factory;}void cmExtraCodeLiteGenerator::Generate(){  // Hold root tree information for creating the workspace  std::string workspaceProjectName;  std::string workspaceOutputDir;  std::string workspaceFileName;  std::string workspaceSourcePath;  const std::map<std::string, std::vector<cmLocalGenerator*> >& projectMap =    this->GlobalGenerator->GetProjectMap();  // loop projects and locate the root project.  // and extract the information for creating the worspace  // root makefile  for (std::map<std::string, std::vector<cmLocalGenerator*> >::const_iterator         it = projectMap.begin();       it != projectMap.end(); ++it) {    const cmMakefile* mf = it->second[0]->GetMakefile();    this->ConfigName = GetConfigurationName(mf);    if (strcmp(it->second[0]->GetCurrentBinaryDirectory(),               it->second[0]->GetBinaryDirectory()) == 0) {      workspaceOutputDir = it->second[0]->GetCurrentBinaryDirectory();      workspaceProjectName = it->second[0]->GetProjectName();      workspaceSourcePath = it->second[0]->GetSourceDirectory();      workspaceFileName = workspaceOutputDir + "/";      workspaceFileName += workspaceProjectName + ".workspace";      this->WorkspacePath = it->second[0]->GetCurrentBinaryDirectory();      ;      break;    }  }  cmGeneratedFileStream fout(workspaceFileName.c_str());  cmXMLWriter xml(fout);  xml.StartDocument("utf-8");  xml.StartElement("CodeLite_Workspace");  xml.Attribute("Name", workspaceProjectName);  bool const targetsAreProjects =    this->GlobalGenerator->GlobalSettingIsOn("CMAKE_CODELITE_USE_TARGETS");  std::vector<std::string> ProjectNames;  if (targetsAreProjects) {    ProjectNames = CreateProjectsByTarget(&xml);  } else {    ProjectNames = CreateProjectsByProjectMaps(&xml);  }  xml.StartElement("BuildMatrix");  xml.StartElement("WorkspaceConfiguration");  xml.Attribute("Name", this->ConfigName);  xml.Attribute("Selected", "yes");  for (std::vector<std::string>::iterator it(ProjectNames.begin());       it != ProjectNames.end(); it++) {    xml.StartElement("Project");    xml.Attribute("Name", *it);    xml.Attribute("ConfigName", this->ConfigName);    xml.EndElement();  }  xml.EndElement(); // WorkspaceConfiguration  xml.EndElement(); // BuildMatrix  xml.EndElement(); // CodeLite_Workspace}// Create projects where targets are the projectsstd::vector<std::string> cmExtraCodeLiteGenerator::CreateProjectsByTarget(  cmXMLWriter* xml){  std::vector<std::string> retval;  // for each target in the workspace create a codelite project  const std::vector<cmLocalGenerator*>& lgs =    this->GlobalGenerator->GetLocalGenerators();  for (std::vector<cmLocalGenerator*>::const_iterator lg(lgs.begin());       lg != lgs.end(); lg++) {    for (std::vector<cmGeneratorTarget*>::const_iterator lt =           (*lg)->GetGeneratorTargets().begin();         lt != (*lg)->GetGeneratorTargets().end(); lt++) {      cmState::TargetType type = (*lt)->GetType();      std::string outputDir = (*lg)->GetCurrentBinaryDirectory();      std::string filename = outputDir + "/" + (*lt)->GetName() + ".project";      retval.push_back((*lt)->GetName());      // Make the project file relative to the workspace      std::string relafilename = cmSystemTools::RelativePath(        this->WorkspacePath.c_str(), filename.c_str());      std::string visualname = (*lt)->GetName();      switch (type) {        case cmState::SHARED_LIBRARY:        case cmState::STATIC_LIBRARY:        case cmState::MODULE_LIBRARY:          visualname = "lib" + visualname;        case cmState::EXECUTABLE:          xml->StartElement("Project");          xml->Attribute("Name", visualname);          xml->Attribute("Path", relafilename);          xml->Attribute("Active", "No");          xml->EndElement();          CreateNewProjectFile(*lt, filename);          break;        default:          break;      }    }  }  return retval;}// The "older way of doing it.std::vector<std::string> cmExtraCodeLiteGenerator::CreateProjectsByProjectMaps(  cmXMLWriter* xml){  std::vector<std::string> retval;  // for each sub project in the workspace create a codelite project  for (std::map<std::string, std::vector<cmLocalGenerator*> >::const_iterator         it = this->GlobalGenerator->GetProjectMap().begin();       it != this->GlobalGenerator->GetProjectMap().end(); it++) {    std::string outputDir = it->second[0]->GetCurrentBinaryDirectory();    std::string projectName = it->second[0]->GetProjectName();    retval.push_back(projectName);    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);    xml->StartElement("Project");    xml->Attribute("Name", projectName);    xml->Attribute("Path", filename);    xml->Attribute("Active", "No");    xml->EndElement();  }  return retval;}/* create the project file */void cmExtraCodeLiteGenerator::CreateProjectFile(  const std::vector<cmLocalGenerator*>& lgs){  std::string outputDir = lgs[0]->GetCurrentBinaryDirectory();  std::string projectName = lgs[0]->GetProjectName();  std::string filename = outputDir + "/";  filename += projectName + ".project";  this->CreateNewProjectFile(lgs, filename);}std::string cmExtraCodeLiteGenerator::CollectSourceFiles(  const cmMakefile* makefile, const cmGeneratorTarget* gt,  std::map<std::string, cmSourceFile*>& cFiles,  std::set<std::string>& otherFiles){  const std::vector<std::string>& srcExts =    this->GlobalGenerator->GetCMakeInstance()->GetSourceExtensions();  std::string projectType;  switch (gt->GetType()) {    case cmState::EXECUTABLE: {      projectType = "Executable";    } break;    case cmState::STATIC_LIBRARY: {      projectType = "Static Library";    } break;    case cmState::SHARED_LIBRARY: {      projectType = "Dynamic Library";    } break;    case cmState::MODULE_LIBRARY: {      projectType = "Dynamic Library";    } break;    default: // intended fallthrough      break;  }  switch (gt->GetType()) {    case cmState::EXECUTABLE:    case cmState::STATIC_LIBRARY:    case cmState::SHARED_LIBRARY:    case cmState::MODULE_LIBRARY: {      std::vector<cmSourceFile*> sources;      gt->GetSourceFiles(sources,                         makefile->GetSafeDefinition("CMAKE_BUILD_TYPE"));      for (std::vector<cmSourceFile*>::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<std::string>::const_iterator ext = srcExts.begin();               ext != srcExts.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;  }  return projectType;}void cmExtraCodeLiteGenerator::CreateNewProjectFile(  const std::vector<cmLocalGenerator*>& lgs, const std::string& filename){  const cmMakefile* mf = lgs[0]->GetMakefile();  cmGeneratedFileStream fout(filename.c_str());  if (!fout) {    return;  }  cmXMLWriter xml(fout);  ////////////////////////////////////  xml.StartDocument("utf-8");  xml.StartElement("CodeLite_Project");  xml.Attribute("Name", lgs[0]->GetProjectName());  xml.Attribute("InternalType", "");  std::string projectType;  // 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::map<std::string, cmSourceFile*> cFiles;  std::set<std::string> otherFiles;  for (std::vector<cmLocalGenerator*>::const_iterator lg = lgs.begin();       lg != lgs.end(); lg++) {    cmMakefile* makefile = (*lg)->GetMakefile();    std::vector<cmGeneratorTarget*> targets = (*lg)->GetGeneratorTargets();    for (std::vector<cmGeneratorTarget*>::iterator ti = targets.begin();         ti != targets.end(); ti++) {      projectType = CollectSourceFiles(makefile, *ti, cFiles, otherFiles);    }  }  // Get the project path ( we need it later to convert files to  // their relative path)  std::string projectPath = cmSystemTools::GetFilenamePath(filename);  CreateProjectSourceEntries(cFiles, otherFiles, &xml, projectPath, mf,                             projectType);  xml.EndElement(); // CodeLite_Project}void cmExtraCodeLiteGenerator::FindMatchingHeaderfiles(  std::map<std::string, cmSourceFile*>& cFiles,  std::set<std::string>& otherFiles){  const std::vector<std::string>& headerExts =    this->GlobalGenerator->GetCMakeInstance()->GetHeaderExtensions();  // 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<std::string, cmSourceFile*>::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<std::string>::const_iterator ext = headerExts.begin();         ext != headerExts.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<std::string>::const_iterator headerIt = otherFiles.find(hname);      if (headerIt != otherFiles.end()) {        break;      }      if (cmSystemTools::FileExists(hname.c_str())) {        otherFiles.insert(hname);        break;      }    }  }}void cmExtraCodeLiteGenerator::CreateProjectSourceEntries(  std::map<std::string, cmSourceFile*>& cFiles,  std::set<std::string>& otherFiles, cmXMLWriter* _xml,  const std::string& projectPath, const cmMakefile* mf,  const std::string& projectType){  cmXMLWriter& xml(*_xml);  FindMatchingHeaderfiles(cFiles, otherFiles);  // Create 2 virtual folders: src and include  // and place all the implementation files into the src  // folder, the rest goes to the include folder  xml.StartElement("VirtualDirectory");  xml.Attribute("Name", "src");  // insert all source files in the codelite project  // first the C/C++ implementation files, then all others  for (std::map<std::string, cmSourceFile*>::const_iterator sit =         cFiles.begin();       sit != cFiles.end(); ++sit) {    xml.StartElement("File");    std::string fpath(sit->first);    std::string frelapath =      cmSystemTools::RelativePath(projectPath.c_str(), sit->first.c_str());    xml.Attribute("Name", frelapath);    xml.EndElement();  }  xml.EndElement(); // VirtualDirectory  xml.StartElement("VirtualDirectory");  xml.Attribute("Name", "include");  for (std::set<std::string>::const_iterator sit = otherFiles.begin();       sit != otherFiles.end(); ++sit) {    xml.StartElement("File");    xml.Attribute(      "Name", cmSystemTools::RelativePath(projectPath.c_str(), sit->c_str()));    xml.EndElement();  }  xml.EndElement(); // VirtualDirectory  // 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 codeliteCompilerName = this->GetCodeLiteCompilerName(mf);  xml.StartElement("Settings");  xml.Attribute("Type", projectType);  xml.StartElement("Configuration");  xml.Attribute("Name", this->ConfigName);  xml.Attribute("CompilerType", this->GetCodeLiteCompilerName(mf));  xml.Attribute("DebuggerType", "GNU gdb debugger");  xml.Attribute("Type", projectType);  xml.Attribute("BuildCmpWithGlobalSettings", "append");  xml.Attribute("BuildLnkWithGlobalSettings", "append");  xml.Attribute("BuildResWithGlobalSettings", "append");  xml.StartElement("Compiler");  xml.Attribute("Options", "-g");  xml.Attribute("Required", "yes");  xml.Attribute("PreCompiledHeader", "");  xml.StartElement("IncludePath");  xml.Attribute("Value", ".");  xml.EndElement(); // IncludePath  xml.EndElement(); // Compiler  xml.StartElement("Linker");  xml.Attribute("Options", "");  xml.Attribute("Required", "yes");  xml.EndElement(); // Linker  xml.StartElement("ResourceCompiler");  xml.Attribute("Options", "");  xml.Attribute("Required", "no");  xml.EndElement(); // ResourceCompiler  xml.StartElement("General");  std::string outputPath = mf->GetSafeDefinition("EXECUTABLE_OUTPUT_PATH");  if (!outputPath.empty())    xml.Attribute("OutputFile", outputPath + "/$(ProjectName)");  else    xml.Attribute("OutputFile", "$(IntermediateDirectory)/$(ProjectName)");  xml.Attribute("IntermediateDirectory", "./");  xml.Attribute("Command", "./$(ProjectName)");  xml.Attribute("CommandArguments", "");  if (!outputPath.empty())    xml.Attribute("WorkingDirectory", outputPath);  else    xml.Attribute("WorkingDirectory", "$(IntermediateDirectory)");  xml.Attribute("PauseExecWhenProcTerminates", "yes");  xml.EndElement(); // General  xml.StartElement("Debugger");  xml.Attribute("IsRemote", "no");  xml.Attribute("RemoteHostName", "");  xml.Attribute("RemoteHostPort", "");  xml.Attribute("DebuggerPath", "");  xml.Element("PostConnectCommands");  xml.Element("StartupCommands");  xml.EndElement(); // Debugger  xml.Element("PreBuild");  xml.Element("PostBuild");  xml.StartElement("CustomBuild");  xml.Attribute("Enabled", "yes");  xml.Element("RebuildCommand", GetRebuildCommand(mf));  xml.Element("CleanCommand", GetCleanCommand(mf));  xml.Element("BuildCommand", GetBuildCommand(mf));  xml.Element("SingleFileCommand", GetSingleFileBuildCommand(mf));  xml.Element("PreprocessFileCommand");  xml.Element("WorkingDirectory", "$(WorkspacePath)");  xml.EndElement(); // CustomBuild  xml.StartElement("AdditionalRules");  xml.Element("CustomPostBuild");  xml.Element("CustomPreBuild");  xml.EndElement(); // AdditionalRules  xml.EndElement(); // Configuration  xml.StartElement("GlobalSettings");  xml.StartElement("Compiler");  xml.Attribute("Options", "");  xml.StartElement("IncludePath");  xml.Attribute("Value", ".");  xml.EndElement(); // IncludePath  xml.EndElement(); // Compiler  xml.StartElement("Linker");  xml.Attribute("Options", "");  xml.StartElement("LibraryPath");  xml.Attribute("Value", ".");  xml.EndElement(); // LibraryPath  xml.EndElement(); // Linker  xml.StartElement("ResourceCompiler");  xml.Attribute("Options", "");  xml.EndElement(); // ResourceCompiler  xml.EndElement(); // GlobalSettings  xml.EndElement(); // Settings}void cmExtraCodeLiteGenerator::CreateNewProjectFile(  const cmGeneratorTarget* gt, const std::string& filename){  const cmMakefile* mf = gt->Makefile;  cmGeneratedFileStream fout(filename.c_str());  if (!fout) {    return;  }  cmXMLWriter xml(fout);  ////////////////////////////////////  xml.StartDocument("utf-8");  xml.StartElement("CodeLite_Project");  std::string visualname = gt->GetName();  switch (gt->GetType()) {    case cmState::STATIC_LIBRARY:    case cmState::SHARED_LIBRARY:    case cmState::MODULE_LIBRARY:      visualname = "lib" + visualname;    default: // intended fallthrough      break;  }  xml.Attribute("Name", visualname);  xml.Attribute("InternalType", "");  // 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::vector<std::string> headerExts =    this->GlobalGenerator->GetCMakeInstance()->GetHeaderExtensions();  std::map<std::string, cmSourceFile*> cFiles;  std::set<std::string> otherFiles;  projectType = CollectSourceFiles(mf, gt, cFiles, otherFiles);  // Get the project path ( we need it later to convert files to  // their relative path)  std::string projectPath = cmSystemTools::GetFilenamePath(filename);  CreateProjectSourceEntries(cFiles, otherFiles, &xml, projectPath, mf,                             projectType);  xml.EndElement(); // CodeLite_Project}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")) {    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) + " && " + 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;}
 |