/*============================================================================ 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 #include #include 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 >::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& 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::const_iterator lg=lgs.begin(); lg!=lgs.end(); lg++) { std::vector const& targets = (*lg)->GetGeneratorTargets(); for (std::vector::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& 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 files; std::string tmp; std::vector hdrExts = this->GlobalGenerator->GetCMakeInstance()->GetHeaderExtensions(); for (std::vector::const_iterator it=lgs.begin(); it!=lgs.end(); it++) { cmMakefile* makefile=(*it)->GetMakefile(); const std::vector& listFiles=makefile->GetListFiles(); for (std::vector::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 std::vector targets=(*it)->GetGeneratorTargets(); for (std::vector::iterator ti = targets.begin(); ti != targets.end(); ti++) { std::vector sources; cmGeneratorTarget* gt = *ti; gt->GetSourceFiles(sources, gt->Target->GetMakefile() ->GetSafeDefinition("CMAKE_BUILD_TYPE")); for (std::vector::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::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::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); } } } } //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::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.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)) { 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 lines; while (cmSystemTools::GetLineFromStream(oldProjectFile, tmp)) { lines.push_back(tmp); } oldProjectFile.close(); cmGeneratedFileStream fout(filename.c_str()); if(!fout) { return; } for (std::vector::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, "")!=0) || (strstr(line, "")!=0) || (strstr(line, "")!=0) || (strstr(line, "")!=0) || (strstr(line, "")!=0) || (strstr(line, "")!=0)) { continue; } // output the line from the file if it is not one of the above tags fout<<*it<<"\n"; // if this is the tag output the stuff that goes in the // general tag if (strstr(line, "")) { fout<< " KDevCustomProject\n"; fout<< " " <\n"; //this one is important fout<<" true\n"; //and this one } // inside kdevcustomproject the must be put if (strstr(line, "")) { fout<<" "<\n"; } // buildtool and builddir go inside if (strstr(line, "")) { fout<<" make\n"; fout<<" "<\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::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; }