123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602 |
- /*============================================================================
- CMake - Cross Platform Makefile Generator
- Copyright 2004-2009 Kitware, Inc.
- Copyright 2004 Alexander Neundorf ([email protected])
- 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()) ==
- 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<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()) ==
- 0) &&
- (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()) ==
- 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<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>") != 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
- << "</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;
- }
|