diff options
Diffstat (limited to 'Source/cmGlobalKdevelopGenerator.cxx')
-rw-r--r-- | Source/cmGlobalKdevelopGenerator.cxx | 619 |
1 files changed, 619 insertions, 0 deletions
diff --git a/Source/cmGlobalKdevelopGenerator.cxx b/Source/cmGlobalKdevelopGenerator.cxx new file mode 100644 index 000000000..f69944847 --- /dev/null +++ b/Source/cmGlobalKdevelopGenerator.cxx @@ -0,0 +1,619 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2004-2009 Kitware, Inc. + Copyright 2004 Alexander Neundorf (neundorf@kde.org) + + 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 "cmGlobalKdevelopGenerator.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/Directory.hxx> + +//---------------------------------------------------------------------------- +void cmGlobalKdevelopGenerator +::GetDocumentation(cmDocumentationEntry& entry, const char*) const +{ + entry.Name = this->GetName(); + entry.Brief = "Generates KDevelop 3 project files."; + entry.Full = + "Project files for KDevelop 3 will be created in the top directory " + "and in every subdirectory which features a CMakeLists.txt file " + "containing a PROJECT() call. " + "If you change the settings using KDevelop cmake will try its best " + "to keep your changes when regenerating the project files. " + "Additionally a hierarchy of UNIX makefiles is generated into the " + "build tree. Any " + "standard UNIX-style make program can build the project through the " + "default make target. A \"make install\" target is also provided."; +} + +cmGlobalKdevelopGenerator::cmGlobalKdevelopGenerator() +:cmExternalMakefileProjectGenerator() +{ + this->SupportedGlobalGenerators.push_back("Unix Makefiles"); +#ifdef CMAKE_USE_NINJA + this->SupportedGlobalGenerators.push_back("Ninja"); +#endif +} + +void cmGlobalKdevelopGenerator::Generate() +{ + // for each sub project in the project create + // a kdevelop project + for (std::map<cmStdString, std::vector<cmLocalGenerator*> >::const_iterator + it = this->GlobalGenerator->GetProjectMap().begin(); + it!= this->GlobalGenerator->GetProjectMap().end(); + ++it) + { + cmMakefile* mf = it->second[0]->GetMakefile(); + std::string outputDir=mf->GetStartOutputDirectory(); + std::string projectDir=mf->GetHomeDirectory(); + std::string projectName=mf->GetProjectName(); + std::string cmakeFilePattern("CMakeLists.txt;*.cmake;"); + std::string fileToOpen; + const std::vector<cmLocalGenerator*>& lgs= it->second; + // create the project.kdevelop.filelist file + if(!this->CreateFilelistFile(lgs, outputDir, projectDir, + projectName, cmakeFilePattern, fileToOpen)) + { + cmSystemTools::Error("Can not create filelist file"); + return; + } + //try to find the name of an executable so we have something to + //run from kdevelop for now just pick the first executable found + std::string executable; + 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++) + { + if (ti->second.GetType()==cmTarget::EXECUTABLE) + { + executable = ti->second.GetProperty("LOCATION"); + break; + } + } + if (!executable.empty()) + { + break; + } + } + + // now create a project file + this->CreateProjectFile(outputDir, projectDir, projectName, + executable, cmakeFilePattern, fileToOpen); + } +} + +bool cmGlobalKdevelopGenerator +::CreateFilelistFile(const std::vector<cmLocalGenerator*>& lgs, + const std::string& outputDir, + const std::string& projectDirIn, + const std::string& projectname, + std::string& cmakeFilePattern, + std::string& fileToOpen) +{ + std::string projectDir = projectDirIn + "/"; + std::string filename = outputDir+ "/" + projectname +".kdevelop.filelist"; + + std::set<cmStdString> files; + std::string tmp; + + for (std::vector<cmLocalGenerator*>::const_iterator it=lgs.begin(); + it!=lgs.end(); it++) + { + cmMakefile* makefile=(*it)->GetMakefile(); + const std::vector<std::string>& listFiles=makefile->GetListFiles(); + for (std::vector<std::string>::const_iterator lt=listFiles.begin(); + lt!=listFiles.end(); lt++) + { + tmp=*lt; + cmSystemTools::ReplaceString(tmp, projectDir.c_str(), ""); + // make sure the file is part of this source tree + if ((tmp[0]!='/') && + (strstr(tmp.c_str(), + cmake::GetCMakeFilesDirectoryPostSlash())==0)) + { + files.insert(tmp); + tmp=cmSystemTools::GetFilenameName(tmp); + //add all files which dont match the default + // */CMakeLists.txt;*cmake; to the file pattern + if ((tmp!="CMakeLists.txt") + && (strstr(tmp.c_str(), ".cmake")==0)) + { + cmakeFilePattern+=tmp+";"; + } + } + } + + //get all sources + cmTargets& targets=makefile->GetTargets(); + for (cmTargets::iterator ti = targets.begin(); + ti != targets.end(); ti++) + { + const std::vector<cmSourceFile*>& sources=ti->second.GetSourceFiles(); + for (std::vector<cmSourceFile*>::const_iterator si=sources.begin(); + si!=sources.end(); si++) + { + tmp=(*si)->GetFullPath(); + std::string headerBasename=cmSystemTools::GetFilenamePath(tmp); + headerBasename+="/"; + headerBasename+=cmSystemTools::GetFilenameWithoutExtension(tmp); + + cmSystemTools::ReplaceString(tmp, projectDir.c_str(), ""); + + if ((tmp[0]!='/') && + (strstr(tmp.c_str(), + cmake::GetCMakeFilesDirectoryPostSlash())==0) && + (cmSystemTools::GetFilenameExtension(tmp)!=".moc")) + { + files.insert(tmp); + + // check if there's a matching header around + for(std::vector<std::string>::const_iterator + ext = makefile->GetHeaderExtensions().begin(); + ext != makefile->GetHeaderExtensions().end(); ++ext) + { + std::string hname=headerBasename; + hname += "."; + hname += *ext; + if(cmSystemTools::FileExists(hname.c_str())) + { + cmSystemTools::ReplaceString(hname, projectDir.c_str(), ""); + files.insert(hname); + break; + } + } + } + } + for (std::vector<std::string>::const_iterator lt=listFiles.begin(); + lt!=listFiles.end(); lt++) + { + tmp=*lt; + cmSystemTools::ReplaceString(tmp, projectDir.c_str(), ""); + if ((tmp[0]!='/') && + (strstr(tmp.c_str(), + cmake::GetCMakeFilesDirectoryPostSlash())==0)) + { + files.insert(tmp.c_str()); + } + } + } + } + + //check if the output file already exists and read it + //insert all files which exist into the set of files + std::ifstream oldFilelist(filename.c_str()); + if (oldFilelist) + { + while (cmSystemTools::GetLineFromStream(oldFilelist, tmp)) + { + if (tmp[0]=='/') + { + continue; + } + std::string completePath=projectDir+tmp; + if (cmSystemTools::FileExists(completePath.c_str())) + { + files.insert(tmp); + } + } + oldFilelist.close(); + } + + //now write the new filename + cmGeneratedFileStream fout(filename.c_str()); + if(!fout) + { + return false; + } + + fileToOpen=""; + for (std::set<cmStdString>::const_iterator it=files.begin(); + it!=files.end(); it++) + { + // get the full path to the file + tmp=cmSystemTools::CollapseFullPath(it->c_str(), projectDir.c_str()); + // just select the first source file + if (fileToOpen.empty()) + { + std::string ext = cmSystemTools::GetFilenameExtension(tmp); + if ((ext==".c") || (ext==".cc") || (ext==".cpp") || (ext==".cxx") + || (ext==".C") || (ext==".h") || (ext==".hpp")) + { + fileToOpen=tmp; + } + } + // make it relative to the project dir + cmSystemTools::ReplaceString(tmp, projectDir.c_str(), ""); + // only put relative paths + if (tmp.size() && tmp[0] != '/') + { + fout << tmp.c_str() <<"\n"; + } + } + return true; +} + + +/* create the project file, if it already exists, merge it with the +existing one, otherwise create a new one */ +void cmGlobalKdevelopGenerator +::CreateProjectFile(const std::string& outputDir, + const std::string& projectDir, + const std::string& projectname, + const std::string& executable, + const std::string& cmakeFilePattern, + const std::string& fileToOpen) +{ + this->Blacklist.clear(); + + std::string filename=outputDir+"/"; + filename+=projectname+".kdevelop"; + std::string sessionFilename=outputDir+"/"; + sessionFilename+=projectname+".kdevses"; + + if (cmSystemTools::FileExists(filename.c_str())) + { + this->MergeProjectFiles(outputDir, projectDir, filename, + executable, cmakeFilePattern, + fileToOpen, sessionFilename); + } + else + { + // add all subdirectories which are cmake build directories to the + // kdevelop blacklist so they are not monitored for added or removed files + // since this is handled by adding files to the cmake files + cmsys::Directory d; + if (d.Load(projectDir.c_str())) + { + size_t numf = d.GetNumberOfFiles(); + for (unsigned int i = 0; i < numf; i++) + { + std::string nextFile = d.GetFile(i); + if ((nextFile!=".") && (nextFile!="..")) + { + std::string tmp = projectDir; + tmp += "/"; + tmp += nextFile; + if (cmSystemTools::FileIsDirectory(tmp.c_str())) + { + tmp += "/CMakeCache.txt"; + if ((nextFile == "CMakeFiles") + || (cmSystemTools::FileExists(tmp.c_str()))) + { + this->Blacklist.push_back(nextFile); + } + } + } + } + } + this->CreateNewProjectFile(outputDir, projectDir, filename, + executable, cmakeFilePattern, + fileToOpen, sessionFilename); + } + +} + +void cmGlobalKdevelopGenerator +::MergeProjectFiles(const std::string& outputDir, + const std::string& projectDir, + const std::string& filename, + const std::string& executable, + const std::string& cmakeFilePattern, + const std::string& fileToOpen, + const std::string& sessionFilename) +{ + std::ifstream oldProjectFile(filename.c_str()); + if (!oldProjectFile) + { + this->CreateNewProjectFile(outputDir, projectDir, filename, + executable, cmakeFilePattern, + fileToOpen, sessionFilename); + return; + } + + /* Read the existing project file (line by line), copy all lines + into the new project file, except the ones which can be reliably + set from contents of the CMakeLists.txt */ + std::string tmp; + std::vector<std::string> lines; + while (cmSystemTools::GetLineFromStream(oldProjectFile, tmp)) + { + lines.push_back(tmp); + } + oldProjectFile.close(); + + cmGeneratedFileStream fout(filename.c_str()); + if(!fout) + { + return; + } + + for (std::vector<std::string>::const_iterator it=lines.begin(); + it!=lines.end(); it++) + { + const char* line=(*it).c_str(); + // skip these tags as they are always replaced + if ((strstr(line, "<projectdirectory>")!=0) + || (strstr(line, "<projectmanagement>")!=0) + || (strstr(line, "<absoluteprojectpath>")!=0) + || (strstr(line, "<filelistdirectory>")!=0) + || (strstr(line, "<buildtool>")!=0) + || (strstr(line, "<builddir>")!=0)) + { + continue; + } + + // output the line from the file if it is not one of the above tags + fout<<*it<<"\n"; + // if this is the <general> tag output the stuff that goes in the + // general tag + if (strstr(line, "<general>")) + { + fout<< " <projectmanagement>KDevCustomProject</projectmanagement>\n"; + fout<< " <projectdirectory>" <<projectDir.c_str() + << "</projectdirectory>\n"; //this one is important + fout<<" <absoluteprojectpath>true</absoluteprojectpath>\n"; + //and this one + } + // inside kdevcustomproject the <filelistdirectory> must be put + if (strstr(line, "<kdevcustomproject>")) + { + fout<<" <filelistdirectory>"<<outputDir.c_str() + <<"</filelistdirectory>\n"; + } + // buildtool and builddir go inside <build> + if (strstr(line, "<build>")) + { + fout<<" <buildtool>make</buildtool>\n"; + fout<<" <builddir>"<<outputDir.c_str()<<"</builddir>\n"; + } + } +} + +void cmGlobalKdevelopGenerator +::CreateNewProjectFile(const std::string& outputDir, + const std::string& projectDir, + const std::string& filename, + const std::string& executable, + const std::string& cmakeFilePattern, + const std::string& fileToOpen, + const std::string& sessionFilename) +{ + cmGeneratedFileStream fout(filename.c_str()); + if(!fout) + { + return; + } + + // check for a version control system + bool hasSvn = cmSystemTools::FileExists((projectDir + "/.svn").c_str()); + bool hasCvs = cmSystemTools::FileExists((projectDir + "/CVS").c_str()); + + bool enableCxx = (this->GlobalGenerator->GetLanguageEnabled("C") + || this->GlobalGenerator->GetLanguageEnabled("CXX")); + bool enableFortran = this->GlobalGenerator->GetLanguageEnabled("Fortran"); + std::string primaryLanguage = "C++"; + if (enableFortran && !enableCxx) + { + primaryLanguage="Fortran77"; + } + + fout<<"<?xml version = '1.0'?>\n" + "<kdevelop>\n" + " <general>\n" + " <author></author>\n" + " <email></email>\n" + " <version>$VERSION$</version>\n" + " <projectmanagement>KDevCustomProject</projectmanagement>\n" + " <primarylanguage>" << primaryLanguage << "</primarylanguage>\n" + " <ignoreparts/>\n" + " <projectdirectory>" << projectDir.c_str() << + "</projectdirectory>\n"; //this one is important + fout<<" <absoluteprojectpath>true</absoluteprojectpath>\n"; //and this one + + // setup additional languages + fout<<" <secondaryLanguages>\n"; + if (enableFortran && enableCxx) + { + fout<<" <language>Fortran</language>\n"; + } + if (enableCxx) + { + fout<<" <language>C</language>\n"; + } + fout<<" </secondaryLanguages>\n"; + + if (hasSvn) + { + fout << " <versioncontrol>kdevsubversion</versioncontrol>\n"; + } + else if (hasCvs) + { + fout << " <versioncontrol>kdevcvsservice</versioncontrol>\n"; + } + + fout<<" </general>\n" + " <kdevcustomproject>\n" + " <filelistdirectory>" << outputDir.c_str() << + "</filelistdirectory>\n" + " <run>\n" + " <mainprogram>" << executable.c_str() << "</mainprogram>\n" + " <directoryradio>custom</directoryradio>\n" + " <customdirectory>"<<outputDir.c_str()<<"</customdirectory>\n" + " <programargs></programargs>\n" + " <terminal>false</terminal>\n" + " <autocompile>true</autocompile>\n" + " <envvars/>\n" + " </run>\n" + " <build>\n" + " <buildtool>make</buildtool>\n"; //this one is important + fout<<" <builddir>"<<outputDir.c_str()<<"</builddir>\n"; //and this one + fout<<" </build>\n" + " <make>\n" + " <abortonerror>false</abortonerror>\n" + " <numberofjobs>1</numberofjobs>\n" + " <dontact>false</dontact>\n" + " <makebin>" << this->GlobalGenerator->GetLocalGenerators()[0]-> + GetMakefile()->GetRequiredDefinition("CMAKE_BUILD_TOOL") + << " </makebin>\n" + " <selectedenvironment>default</selectedenvironment>\n" + " <environments>\n" + " <default>\n" + " <envvar value=\"1\" name=\"VERBOSE\" />\n" + " <envvar value=\"1\" name=\"CMAKE_NO_VERBOSE\" />\n" + " </default>\n" + " </environments>\n" + " </make>\n"; + + fout<<" <blacklist>\n"; + for(std::vector<std::string>::const_iterator dirIt=this->Blacklist.begin(); + dirIt != this->Blacklist.end(); + ++dirIt) + { + fout<<" <path>" << dirIt->c_str() << "</path>\n"; + } + fout<<" </blacklist>\n"; + + fout<<" </kdevcustomproject>\n" + " <kdevfilecreate>\n" + " <filetypes/>\n" + " <useglobaltypes>\n" + " <type ext=\"ui\" />\n" + " <type ext=\"cpp\" />\n" + " <type ext=\"h\" />\n" + " </useglobaltypes>\n" + " </kdevfilecreate>\n" + " <kdevdoctreeview>\n" + " <projectdoc>\n" + " <userdocDir>html/</userdocDir>\n" + " <apidocDir>html/</apidocDir>\n" + " </projectdoc>\n" + " <ignoreqt_xml/>\n" + " <ignoredoxygen/>\n" + " <ignorekdocs/>\n" + " <ignoretocs/>\n" + " <ignoredevhelp/>\n" + " </kdevdoctreeview>\n"; + + if (enableCxx) + { + fout<<" <cppsupportpart>\n" + " <filetemplates>\n" + " <interfacesuffix>.h</interfacesuffix>\n" + " <implementationsuffix>.cpp</implementationsuffix>\n" + " </filetemplates>\n" + " </cppsupportpart>\n" + " <kdevcppsupport>\n" + " <codecompletion>\n" + " <includeGlobalFunctions>true</includeGlobalFunctions>\n" + " <includeTypes>true</includeTypes>\n" + " <includeEnums>true</includeEnums>\n" + " <includeTypedefs>false</includeTypedefs>\n" + " <automaticCodeCompletion>true</automaticCodeCompletion>\n" + " <automaticArgumentsHint>true</automaticArgumentsHint>\n" + " <automaticHeaderCompletion>true</automaticHeaderCompletion>\n" + " <codeCompletionDelay>250</codeCompletionDelay>\n" + " <argumentsHintDelay>400</argumentsHintDelay>\n" + " <headerCompletionDelay>250</headerCompletionDelay>\n" + " </codecompletion>\n" + " <references/>\n" + " </kdevcppsupport>\n"; + } + + if (enableFortran) + { + fout<<" <kdevfortransupport>\n" + " <ftnchek>\n" + " <division>false</division>\n" + " <extern>false</extern>\n" + " <declare>false</declare>\n" + " <pure>false</pure>\n" + " <argumentsall>false</argumentsall>\n" + " <commonall>false</commonall>\n" + " <truncationall>false</truncationall>\n" + " <usageall>false</usageall>\n" + " <f77all>false</f77all>\n" + " <portabilityall>false</portabilityall>\n" + " <argumentsonly/>\n" + " <commononly/>\n" + " <truncationonly/>\n" + " <usageonly/>\n" + " <f77only/>\n" + " <portabilityonly/>\n" + " </ftnchek>\n" + " </kdevfortransupport>\n"; + } + + // set up file groups. maybe this can be used with the CMake SOURCE_GROUP() + // command + fout<<" <kdevfileview>\n" + " <groups>\n" + " <group pattern=\"" << cmakeFilePattern.c_str() << + "\" name=\"CMake\" />\n"; + + if (enableCxx) + { + fout<<" <group pattern=\"*.h;*.hxx;*.hpp\" name=\"Header\" />\n" + " <group pattern=\"*.c\" name=\"C Sources\" />\n" + " <group pattern=\"*.cpp;*.C;*.cxx;*.cc\" name=\"C++ Sources\"" + "/>\n"; + } + + if (enableFortran) + { + fout<<" <group pattern=\"*.f;*.F;*.f77;*.F77;*.f90;*.F90;*.for;*.f95;" + "*.F95\" name=\"Fortran Sources\" />\n"; + } + + fout<<" <group pattern=\"*.ui\" name=\"Qt Designer files\" />\n" + " <hidenonprojectfiles>true</hidenonprojectfiles>\n" + " </groups>\n" + " <tree>\n" + " <hidepatterns>*.o,*.lo,CVS,*~,cmake*</hidepatterns>\n" + " <hidenonprojectfiles>true</hidenonprojectfiles>\n" + " </tree>\n" + " </kdevfileview>\n" + "</kdevelop>\n"; + + if (sessionFilename.empty()) + { + return; + } + + // and a session file, so that kdevelop opens a file if it opens the + // project the first time + cmGeneratedFileStream devses(sessionFilename.c_str()); + if(!devses) + { + return; + } + devses<<"<?xml version = '1.0' encoding = \'UTF-8\'?>\n" + "<!DOCTYPE KDevPrjSession>\n" + "<KDevPrjSession>\n" + " <DocsAndViews NumberOfDocuments=\"1\" >\n" + " <Doc0 NumberOfViews=\"1\" URL=\"file://" << fileToOpen.c_str() << + "\" >\n" + " <View0 line=\"0\" Type=\"Source\" />\n" + " </Doc0>\n" + " </DocsAndViews>\n" + "</KDevPrjSession>\n"; +} + |