| /*============================================================================ |
| 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 <cmsys/SystemTools.hxx> |
| #include <cmsys/SystemInformation.hxx> |
| #include <cmsys/Directory.hxx> |
| #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<std::string, std::vector<cmLocalGenerator*> >::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 << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" |
| "<CodeLite_Workspace Name=\"" << workspaceProjectName << "\" >\n"; |
| } |
| } |
| |
| // 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) |
| { |
| // 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 << " <Project Name=\"" << projectName << "\" Path=\"" |
| << filename << "\" Active=\"No\"/>\n"; |
| lprjdebug += "<Project Name=\"" + projectName |
| + "\" ConfigName=\"" + this->ConfigName + "\"/>\n"; |
| } |
| |
| fout << " <BuildMatrix>\n" |
| " <WorkspaceConfiguration Name=\"" |
| << this->ConfigName << "\" Selected=\"yes\">\n" |
| " " << lprjdebug << "" |
| " </WorkspaceConfiguration>\n" |
| " </BuildMatrix>\n" |
| "</CodeLite_Workspace>\n"; |
| } |
| |
| /* create the project file */ |
| void cmExtraCodeLiteGenerator::CreateProjectFile( |
| const std::vector<cmLocalGenerator*>& 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<cmLocalGenerator*>& lgs, |
| const std::string& filename) |
| { |
| const cmMakefile* mf=lgs[0]->GetMakefile(); |
| cmGeneratedFileStream fout(filename.c_str()); |
| if(!fout) |
| { |
| return; |
| } |
| |
| //////////////////////////////////// |
| fout << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" |
| "<CodeLite_Project Name=\"" << mf->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<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(); |
| 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<cmSourceFile*> sources; |
| ti->second.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 = 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<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 = 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<std::string>::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<< " <VirtualDirectory Name=\"src\">\n"; |
| |
| // 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) |
| { |
| std::string relativePath = |
| cmSystemTools::RelativePath(projectPath.c_str(), sit->first.c_str()); |
| fout<< " <File Name=\"" << relativePath << "\"/>\n"; |
| } |
| fout<< " </VirtualDirectory>\n"; |
| fout<< " <VirtualDirectory Name=\"include\">\n"; |
| for (std::set<std::string>::const_iterator |
| sit=otherFiles.begin(); |
| sit!=otherFiles.end(); |
| ++sit) |
| { |
| std::string relativePath = |
| cmSystemTools::RelativePath(projectPath.c_str(), sit->c_str()); |
| fout << " <File Name=\"" << relativePath << "\"/>\n"; |
| } |
| fout << " </VirtualDirectory>\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" |
| " <Settings Type=\"" << projectType << "\">\n" |
| " <Configuration Name=\"" << this->ConfigName << "\" CompilerType=\"" |
| << codeliteCompilerName << "\" DebuggerType=\"GNU gdb debugger\" " |
| "Type=\"" |
| << projectType << "\" BuildCmpWithGlobalSettings=\"append\" " |
| "BuildLnkWithGlobalSettings=\"append\" " |
| "BuildResWithGlobalSettings=\"append\">\n" |
| " <Compiler Options=\"-g\" " |
| "Required=\"yes\" PreCompiledHeader=\"\">\n" |
| " <IncludePath Value=\".\"/>\n" |
| " </Compiler>\n" |
| " <Linker Options=\"\" Required=\"yes\"/>\n" |
| " <ResourceCompiler Options=\"\" Required=\"no\"/>\n" |
| " <General OutputFile=\"$(IntermediateDirectory)/$(ProjectName)\" " |
| "IntermediateDirectory=\"./\" Command=\"./$(ProjectName)\" " |
| "CommandArguments=\"\" WorkingDirectory=\"$(IntermediateDirectory)\" " |
| "PauseExecWhenProcTerminates=\"yes\"/>\n" |
| " <Debugger IsRemote=\"no\" RemoteHostName=\"\" " |
| "RemoteHostPort=\"\" DebuggerPath=\"\">\n" |
| " <PostConnectCommands/>\n" |
| " <StartupCommands/>\n" |
| " </Debugger>\n" |
| " <PreBuild/>\n" |
| " <PostBuild/>\n" |
| " <CustomBuild Enabled=\"yes\">\n" |
| " <RebuildCommand>" << rebuildCommand << "</RebuildCommand>\n" |
| " <CleanCommand>" << cleanCommand << "</CleanCommand>\n" |
| " <BuildCommand>" << buildCommand << "</BuildCommand>\n" |
| " <SingleFileCommand>" << singleFileCommand |
| << "</SingleFileCommand>\n" |
| " <PreprocessFileCommand/>\n" |
| " <WorkingDirectory>$(WorkspacePath)</WorkingDirectory>\n" |
| " </CustomBuild>\n" |
| " <AdditionalRules>\n" |
| " <CustomPostBuild/>\n" |
| " <CustomPreBuild/>\n" |
| " </AdditionalRules>\n" |
| " </Configuration>\n" |
| " <GlobalSettings>\n" |
| " <Compiler Options=\"\">\n" |
| " <IncludePath Value=\".\"/>\n" |
| " </Compiler>\n" |
| " <Linker Options=\"\">\n" |
| " <LibraryPath Value=\".\"/>\n" |
| " </Linker>\n" |
| " <ResourceCompiler Options=\"\"/>\n" |
| " </GlobalSettings>\n" |
| " </Settings>\n" |
| "</CodeLite_Project>\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; |
| } |