summaryrefslogtreecommitdiffstats
path: root/Source/cmGlobalKdevelopGenerator.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'Source/cmGlobalKdevelopGenerator.cxx')
-rw-r--r--Source/cmGlobalKdevelopGenerator.cxx602
1 files changed, 602 insertions, 0 deletions
diff --git a/Source/cmGlobalKdevelopGenerator.cxx b/Source/cmGlobalKdevelopGenerator.cxx
new file mode 100644
index 0000000..bbd6baa
--- /dev/null
+++ b/Source/cmGlobalKdevelopGenerator.cxx
@@ -0,0 +1,602 @@
+/*============================================================================
+ 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 "cmGeneratedFileStream.h"
+#include "cmGlobalUnixMakefileGenerator3.h"
+#include "cmLocalUnixMakefileGenerator3.h"
+#include "cmMakefile.h"
+#include "cmSourceFile.h"
+#include "cmSystemTools.h"
+#include "cmXMLWriter.h"
+#include "cmake.h"
+
+#include <cmsys/Directory.hxx>
+#include <cmsys/FStream.hxx>
+#include <cmsys/SystemTools.hxx>
+
+void cmGlobalKdevelopGenerator::GetDocumentation(cmDocumentationEntry& entry,
+ const std::string&) const
+{
+ entry.Name = this->GetName();
+ entry.Brief = "Generates KDevelop 3 project files.";
+}
+
+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<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 projectDir = it->second[0]->GetSourceDirectory();
+ std::string projectName = it->second[0]->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++) {
+ std::vector<cmGeneratorTarget*> const& targets =
+ (*lg)->GetGeneratorTargets();
+ for (std::vector<cmGeneratorTarget*>::const_iterator ti =
+ targets.begin();
+ ti != targets.end(); ti++) {
+ if ((*ti)->GetType() == cmState::EXECUTABLE) {
+ executable = (*ti)->GetLocation("");
+ 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<std::string> files;
+ std::string tmp;
+
+ std::vector<std::string> hdrExts =
+ this->GlobalGenerator->GetCMakeInstance()->GetHeaderExtensions();
+
+ 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()) ==
+ CM_NULLPTR)) {
+ 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") == CM_NULLPTR)) {
+ cmakeFilePattern += tmp + ";";
+ }
+ }
+ }
+
+ // get all sources
+ std::vector<cmGeneratorTarget*> targets = (*it)->GetGeneratorTargets();
+ for (std::vector<cmGeneratorTarget*>::iterator ti = targets.begin();
+ ti != targets.end(); ti++) {
+ std::vector<cmSourceFile*> sources;
+ cmGeneratorTarget* gt = *ti;
+ gt->GetSourceFiles(sources, gt->Target->GetMakefile()->GetSafeDefinition(
+ "CMAKE_BUILD_TYPE"));
+ 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()) ==
+ CM_NULLPTR) &&
+ (cmSystemTools::GetFilenameExtension(tmp) != ".moc")) {
+ files.insert(tmp);
+
+ // check if there's a matching header around
+ for (std::vector<std::string>::const_iterator ext = hdrExts.begin();
+ ext != hdrExts.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()) ==
+ CM_NULLPTR)) {
+ files.insert(tmp);
+ }
+ }
+ }
+ }
+
+ // check if the output file already exists and read it
+ // insert all files which exist into the set of files
+ cmsys::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<std::string>::const_iterator it = files.begin();
+ it != files.end(); it++) {
+ // get the full path to the file
+ tmp = cmSystemTools::CollapseFullPath(*it, 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.empty() && tmp[0] != '/') {
+ fout << tmp << "\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)) {
+ 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)) {
+ 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)
+{
+ cmsys::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>") != CM_NULLPTR) ||
+ (strstr(line, "<projectmanagement>") != CM_NULLPTR) ||
+ (strstr(line, "<absoluteprojectpath>") != CM_NULLPTR) ||
+ (strstr(line, "<filelistdirectory>") != CM_NULLPTR) ||
+ (strstr(line, "<buildtool>") != CM_NULLPTR) ||
+ (strstr(line, "<builddir>") != CM_NULLPTR)) {
+ 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
+ << "</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
+ << "</filelistdirectory>\n";
+ }
+ // buildtool and builddir go inside <build>
+ if (strstr(line, "<build>")) {
+ fout << " <buildtool>make</buildtool>\n";
+ fout << " <builddir>" << outputDir << "</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;
+ }
+ cmXMLWriter xml(fout);
+
+ // 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";
+ }
+
+ xml.StartDocument();
+ xml.StartElement("kdevelop");
+ xml.StartElement("general");
+
+ xml.Element("author", "");
+ xml.Element("email", "");
+ xml.Element("version", "$VERSION$");
+ xml.Element("projectmanagement", "KDevCustomProject");
+ xml.Element("primarylanguage", primaryLanguage);
+ xml.Element("ignoreparts");
+ xml.Element("projectdirectory", projectDir); // this one is important
+ xml.Element("absoluteprojectpath", "true"); // and this one
+
+ // setup additional languages
+ xml.StartElement("secondaryLanguages");
+ if (enableFortran && enableCxx) {
+ xml.Element("language", "Fortran");
+ }
+ if (enableCxx) {
+ xml.Element("language", "C");
+ }
+ xml.EndElement();
+
+ if (hasSvn) {
+ xml.Element("versioncontrol", "kdevsubversion");
+ } else if (hasCvs) {
+ xml.Element("versioncontrol", "kdevcvsservice");
+ }
+
+ xml.EndElement(); // general
+ xml.StartElement("kdevcustomproject");
+
+ xml.Element("filelistdirectory", outputDir);
+
+ xml.StartElement("run");
+ xml.Element("mainprogram", executable);
+ xml.Element("directoryradio", "custom");
+ xml.Element("customdirectory", outputDir);
+ xml.Element("programargs", "");
+ xml.Element("terminal", "false");
+ xml.Element("autocompile", "true");
+ xml.Element("envvars");
+ xml.EndElement();
+
+ xml.StartElement("build");
+ xml.Element("buildtool", "make"); // this one is important
+ xml.Element("builddir", outputDir); // and this one
+ xml.EndElement();
+
+ xml.StartElement("make");
+ xml.Element("abortonerror", "false");
+ xml.Element("numberofjobs", 1);
+ xml.Element("dontact", "false");
+ xml.Element("makebin", this->GlobalGenerator->GetLocalGenerators()[0]
+ ->GetMakefile()
+ ->GetRequiredDefinition("CMAKE_MAKE_PROGRAM"));
+ xml.Element("selectedenvironment", "default");
+
+ xml.StartElement("environments");
+ xml.StartElement("default");
+
+ xml.StartElement("envvar");
+ xml.Attribute("value", 1);
+ xml.Attribute("name", "VERBOSE");
+ xml.EndElement();
+
+ xml.StartElement("envvar");
+ xml.Attribute("value", 1);
+ xml.Attribute("name", "CMAKE_NO_VERBOSE");
+ xml.EndElement();
+
+ xml.EndElement(); // default
+ xml.EndElement(); // environments
+ xml.EndElement(); // make
+
+ xml.StartElement("blacklist");
+ for (std::vector<std::string>::const_iterator dirIt =
+ this->Blacklist.begin();
+ dirIt != this->Blacklist.end(); ++dirIt) {
+ xml.Element("path", *dirIt);
+ }
+ xml.EndElement();
+
+ xml.EndElement(); // kdevcustomproject
+
+ xml.StartElement("kdevfilecreate");
+ xml.Element("filetypes");
+ xml.StartElement("useglobaltypes");
+
+ xml.StartElement("type");
+ xml.Attribute("ext", "ui");
+ xml.EndElement();
+
+ xml.StartElement("type");
+ xml.Attribute("ext", "cpp");
+ xml.EndElement();
+
+ xml.StartElement("type");
+ xml.Attribute("ext", "h");
+ xml.EndElement();
+
+ xml.EndElement(); // useglobaltypes
+ xml.EndElement(); // kdevfilecreate
+
+ xml.StartElement("kdevdoctreeview");
+ xml.StartElement("projectdoc");
+ xml.Element("userdocDir", "html/");
+ xml.Element("apidocDir", "html/");
+ xml.EndElement(); // projectdoc
+ xml.Element("ignoreqt_xml");
+ xml.Element("ignoredoxygen");
+ xml.Element("ignorekdocs");
+ xml.Element("ignoretocs");
+ xml.Element("ignoredevhelp");
+ xml.EndElement(); // kdevdoctreeview;
+
+ if (enableCxx) {
+ xml.StartElement("cppsupportpart");
+ xml.StartElement("filetemplates");
+ xml.Element("interfacesuffix", ".h");
+ xml.Element("implementationsuffix", ".cpp");
+ xml.EndElement(); // filetemplates
+ xml.EndElement(); // cppsupportpart
+
+ xml.StartElement("kdevcppsupport");
+ xml.StartElement("codecompletion");
+ xml.Element("includeGlobalFunctions", "true");
+ xml.Element("includeTypes", "true");
+ xml.Element("includeEnums", "true");
+ xml.Element("includeTypedefs", "false");
+ xml.Element("automaticCodeCompletion", "true");
+ xml.Element("automaticArgumentsHint", "true");
+ xml.Element("automaticHeaderCompletion", "true");
+ xml.Element("codeCompletionDelay", 250);
+ xml.Element("argumentsHintDelay", 400);
+ xml.Element("headerCompletionDelay", 250);
+ xml.EndElement(); // codecompletion
+ xml.Element("references");
+ xml.EndElement(); // kdevcppsupport;
+ }
+
+ if (enableFortran) {
+ xml.StartElement("kdevfortransupport");
+ xml.StartElement("ftnchek");
+ xml.Element("division", "false");
+ xml.Element("extern", "false");
+ xml.Element("declare", "false");
+ xml.Element("pure", "false");
+ xml.Element("argumentsall", "false");
+ xml.Element("commonall", "false");
+ xml.Element("truncationall", "false");
+ xml.Element("usageall", "false");
+ xml.Element("f77all", "false");
+ xml.Element("portabilityall", "false");
+ xml.Element("argumentsonly");
+ xml.Element("commononly");
+ xml.Element("truncationonly");
+ xml.Element("usageonly");
+ xml.Element("f77only");
+ xml.Element("portabilityonly");
+ xml.EndElement(); // ftnchek
+ xml.EndElement(); // kdevfortransupport;
+ }
+
+ // set up file groups. maybe this can be used with the CMake SOURCE_GROUP()
+ // command
+ xml.StartElement("kdevfileview");
+ xml.StartElement("groups");
+
+ xml.StartElement("group");
+ xml.Attribute("pattern", cmakeFilePattern);
+ xml.Attribute("name", "CMake");
+ xml.EndElement();
+
+ if (enableCxx) {
+ xml.StartElement("group");
+ xml.Attribute("pattern", "*.h;*.hxx;*.hpp");
+ xml.Attribute("name", "Header");
+ xml.EndElement();
+
+ xml.StartElement("group");
+ xml.Attribute("pattern", "*.c");
+ xml.Attribute("name", "C Sources");
+ xml.EndElement();
+
+ xml.StartElement("group");
+ xml.Attribute("pattern", "*.cpp;*.C;*.cxx;*.cc");
+ xml.Attribute("name", "C++ Sources");
+ xml.EndElement();
+ }
+
+ if (enableFortran) {
+ xml.StartElement("group");
+ xml.Attribute("pattern",
+ "*.f;*.F;*.f77;*.F77;*.f90;*.F90;*.for;*.f95;*.F95");
+ xml.Attribute("name", "Fortran Sources");
+ xml.EndElement();
+ }
+
+ xml.StartElement("group");
+ xml.Attribute("pattern", "*.ui");
+ xml.Attribute("name", "Qt Designer files");
+ xml.EndElement();
+
+ xml.Element("hidenonprojectfiles", "true");
+ xml.EndElement(); // groups
+
+ xml.StartElement("tree");
+ xml.Element("hidepatterns", "*.o,*.lo,CVS,*~,cmake*");
+ xml.Element("hidenonprojectfiles", "true");
+ xml.EndElement(); // tree
+
+ xml.EndElement(); // kdevfileview
+ xml.EndElement(); // kdevelop;
+ xml.EndDocument();
+
+ 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;
+ }
+ cmXMLWriter sesxml(devses);
+ sesxml.StartDocument("UTF-8");
+ sesxml.Doctype("KDevPrjSession");
+ sesxml.StartElement("KDevPrjSession");
+
+ sesxml.StartElement("DocsAndViews");
+ sesxml.Attribute("NumberOfDocuments", 1);
+
+ sesxml.StartElement("Doc0");
+ sesxml.Attribute("NumberOfViews", 1);
+ sesxml.Attribute("URL", "file://" + fileToOpen);
+
+ sesxml.StartElement("View0");
+ sesxml.Attribute("line", 0);
+ sesxml.Attribute("Type", "Source");
+ sesxml.EndElement(); // View0
+
+ sesxml.EndElement(); // Doc0
+ sesxml.EndElement(); // DocsAndViews
+ sesxml.EndElement(); // KDevPrjSession;
+}