1 /*============================================================================
2 CMake - Cross Platform Makefile Generator
3 Copyright 2004-2009 Kitware, Inc.
4 Copyright 2004 Alexander Neundorf (neundorf@kde.org)
6 Distributed under the OSI-approved BSD License (the "License");
7 see accompanying file Copyright.txt for details.
9 This software is distributed WITHOUT ANY WARRANTY; without even the
10 implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11 See the License for more information.
12 ============================================================================*/
13 #include "cmGlobalKdevelopGenerator.h"
14 #include "cmGlobalUnixMakefileGenerator3.h"
15 #include "cmLocalUnixMakefileGenerator3.h"
16 #include "cmMakefile.h"
18 #include "cmSourceFile.h"
19 #include "cmGeneratedFileStream.h"
20 #include "cmSystemTools.h"
22 #include <cmsys/SystemTools.hxx>
23 #include <cmsys/Directory.hxx>
25 //----------------------------------------------------------------------------
26 void cmGlobalKdevelopGenerator
27 ::GetDocumentation(cmDocumentationEntry& entry, const char*) const
29 entry.Name = this->GetName();
30 entry.Brief = "Generates KDevelop 3 project files.";
32 "Project files for KDevelop 3 will be created in the top directory "
33 "and in every subdirectory which features a CMakeLists.txt file "
34 "containing a PROJECT() call. "
35 "If you change the settings using KDevelop cmake will try its best "
36 "to keep your changes when regenerating the project files. "
37 "Additionally a hierarchy of UNIX makefiles is generated into the "
39 "standard UNIX-style make program can build the project through the "
40 "default make target. A \"make install\" target is also provided.";
43 cmGlobalKdevelopGenerator::cmGlobalKdevelopGenerator()
44 :cmExternalMakefileProjectGenerator()
46 this->SupportedGlobalGenerators.push_back("Unix Makefiles");
47 #ifdef CMAKE_USE_NINJA
48 this->SupportedGlobalGenerators.push_back("Ninja");
52 void cmGlobalKdevelopGenerator::Generate()
54 // for each sub project in the project create
56 for (std::map<cmStdString, std::vector<cmLocalGenerator*> >::const_iterator
57 it = this->GlobalGenerator->GetProjectMap().begin();
58 it!= this->GlobalGenerator->GetProjectMap().end();
61 cmMakefile* mf = it->second[0]->GetMakefile();
62 std::string outputDir=mf->GetStartOutputDirectory();
63 std::string projectDir=mf->GetHomeDirectory();
64 std::string projectName=mf->GetProjectName();
65 std::string cmakeFilePattern("CMakeLists.txt;*.cmake;");
66 std::string fileToOpen;
67 const std::vector<cmLocalGenerator*>& lgs= it->second;
68 // create the project.kdevelop.filelist file
69 if(!this->CreateFilelistFile(lgs, outputDir, projectDir,
70 projectName, cmakeFilePattern, fileToOpen))
72 cmSystemTools::Error("Can not create filelist file");
75 //try to find the name of an executable so we have something to
76 //run from kdevelop for now just pick the first executable found
77 std::string executable;
78 for (std::vector<cmLocalGenerator*>::const_iterator lg=lgs.begin();
81 cmMakefile* makefile=(*lg)->GetMakefile();
82 cmTargets& targets=makefile->GetTargets();
83 for (cmTargets::iterator ti = targets.begin();
84 ti != targets.end(); ti++)
86 if (ti->second.GetType()==cmTarget::EXECUTABLE)
88 executable = ti->second.GetProperty("LOCATION");
92 if (!executable.empty())
98 // now create a project file
99 this->CreateProjectFile(outputDir, projectDir, projectName,
100 executable, cmakeFilePattern, fileToOpen);
104 bool cmGlobalKdevelopGenerator
105 ::CreateFilelistFile(const std::vector<cmLocalGenerator*>& lgs,
106 const std::string& outputDir,
107 const std::string& projectDirIn,
108 const std::string& projectname,
109 std::string& cmakeFilePattern,
110 std::string& fileToOpen)
112 std::string projectDir = projectDirIn + "/";
113 std::string filename = outputDir+ "/" + projectname +".kdevelop.filelist";
115 std::set<cmStdString> files;
118 for (std::vector<cmLocalGenerator*>::const_iterator it=lgs.begin();
121 cmMakefile* makefile=(*it)->GetMakefile();
122 const std::vector<std::string>& listFiles=makefile->GetListFiles();
123 for (std::vector<std::string>::const_iterator lt=listFiles.begin();
124 lt!=listFiles.end(); lt++)
127 cmSystemTools::ReplaceString(tmp, projectDir.c_str(), "");
128 // make sure the file is part of this source tree
131 cmake::GetCMakeFilesDirectoryPostSlash())==0))
134 tmp=cmSystemTools::GetFilenameName(tmp);
135 //add all files which dont match the default
136 // */CMakeLists.txt;*cmake; to the file pattern
137 if ((tmp!="CMakeLists.txt")
138 && (strstr(tmp.c_str(), ".cmake")==0))
140 cmakeFilePattern+=tmp+";";
146 cmTargets& targets=makefile->GetTargets();
147 for (cmTargets::iterator ti = targets.begin();
148 ti != targets.end(); ti++)
150 const std::vector<cmSourceFile*>& sources=ti->second.GetSourceFiles();
151 for (std::vector<cmSourceFile*>::const_iterator si=sources.begin();
152 si!=sources.end(); si++)
154 tmp=(*si)->GetFullPath();
155 std::string headerBasename=cmSystemTools::GetFilenamePath(tmp);
157 headerBasename+=cmSystemTools::GetFilenameWithoutExtension(tmp);
159 cmSystemTools::ReplaceString(tmp, projectDir.c_str(), "");
163 cmake::GetCMakeFilesDirectoryPostSlash())==0) &&
164 (cmSystemTools::GetFilenameExtension(tmp)!=".moc"))
168 // check if there's a matching header around
169 for(std::vector<std::string>::const_iterator
170 ext = makefile->GetHeaderExtensions().begin();
171 ext != makefile->GetHeaderExtensions().end(); ++ext)
173 std::string hname=headerBasename;
176 if(cmSystemTools::FileExists(hname.c_str()))
178 cmSystemTools::ReplaceString(hname, projectDir.c_str(), "");
185 for (std::vector<std::string>::const_iterator lt=listFiles.begin();
186 lt!=listFiles.end(); lt++)
189 cmSystemTools::ReplaceString(tmp, projectDir.c_str(), "");
192 cmake::GetCMakeFilesDirectoryPostSlash())==0))
194 files.insert(tmp.c_str());
200 //check if the output file already exists and read it
201 //insert all files which exist into the set of files
202 std::ifstream oldFilelist(filename.c_str());
205 while (cmSystemTools::GetLineFromStream(oldFilelist, tmp))
211 std::string completePath=projectDir+tmp;
212 if (cmSystemTools::FileExists(completePath.c_str()))
220 //now write the new filename
221 cmGeneratedFileStream fout(filename.c_str());
228 for (std::set<cmStdString>::const_iterator it=files.begin();
229 it!=files.end(); it++)
231 // get the full path to the file
232 tmp=cmSystemTools::CollapseFullPath(it->c_str(), projectDir.c_str());
233 // just select the first source file
234 if (fileToOpen.empty())
236 std::string ext = cmSystemTools::GetFilenameExtension(tmp);
237 if ((ext==".c") || (ext==".cc") || (ext==".cpp") || (ext==".cxx")
238 || (ext==".C") || (ext==".h") || (ext==".hpp"))
243 // make it relative to the project dir
244 cmSystemTools::ReplaceString(tmp, projectDir.c_str(), "");
245 // only put relative paths
246 if (tmp.size() && tmp[0] != '/')
248 fout << tmp.c_str() <<"\n";
255 /* create the project file, if it already exists, merge it with the
256 existing one, otherwise create a new one */
257 void cmGlobalKdevelopGenerator
258 ::CreateProjectFile(const std::string& outputDir,
259 const std::string& projectDir,
260 const std::string& projectname,
261 const std::string& executable,
262 const std::string& cmakeFilePattern,
263 const std::string& fileToOpen)
265 this->Blacklist.clear();
267 std::string filename=outputDir+"/";
268 filename+=projectname+".kdevelop";
269 std::string sessionFilename=outputDir+"/";
270 sessionFilename+=projectname+".kdevses";
272 if (cmSystemTools::FileExists(filename.c_str()))
274 this->MergeProjectFiles(outputDir, projectDir, filename,
275 executable, cmakeFilePattern,
276 fileToOpen, sessionFilename);
280 // add all subdirectories which are cmake build directories to the
281 // kdevelop blacklist so they are not monitored for added or removed files
282 // since this is handled by adding files to the cmake files
284 if (d.Load(projectDir.c_str()))
286 size_t numf = d.GetNumberOfFiles();
287 for (unsigned int i = 0; i < numf; i++)
289 std::string nextFile = d.GetFile(i);
290 if ((nextFile!=".") && (nextFile!=".."))
292 std::string tmp = projectDir;
295 if (cmSystemTools::FileIsDirectory(tmp.c_str()))
297 tmp += "/CMakeCache.txt";
298 if ((nextFile == "CMakeFiles")
299 || (cmSystemTools::FileExists(tmp.c_str())))
301 this->Blacklist.push_back(nextFile);
307 this->CreateNewProjectFile(outputDir, projectDir, filename,
308 executable, cmakeFilePattern,
309 fileToOpen, sessionFilename);
314 void cmGlobalKdevelopGenerator
315 ::MergeProjectFiles(const std::string& outputDir,
316 const std::string& projectDir,
317 const std::string& filename,
318 const std::string& executable,
319 const std::string& cmakeFilePattern,
320 const std::string& fileToOpen,
321 const std::string& sessionFilename)
323 std::ifstream oldProjectFile(filename.c_str());
326 this->CreateNewProjectFile(outputDir, projectDir, filename,
327 executable, cmakeFilePattern,
328 fileToOpen, sessionFilename);
332 /* Read the existing project file (line by line), copy all lines
333 into the new project file, except the ones which can be reliably
334 set from contents of the CMakeLists.txt */
336 std::vector<std::string> lines;
337 while (cmSystemTools::GetLineFromStream(oldProjectFile, tmp))
339 lines.push_back(tmp);
341 oldProjectFile.close();
343 cmGeneratedFileStream fout(filename.c_str());
349 for (std::vector<std::string>::const_iterator it=lines.begin();
350 it!=lines.end(); it++)
352 const char* line=(*it).c_str();
353 // skip these tags as they are always replaced
354 if ((strstr(line, "<projectdirectory>")!=0)
355 || (strstr(line, "<projectmanagement>")!=0)
356 || (strstr(line, "<absoluteprojectpath>")!=0)
357 || (strstr(line, "<filelistdirectory>")!=0)
358 || (strstr(line, "<buildtool>")!=0)
359 || (strstr(line, "<builddir>")!=0))
364 // output the line from the file if it is not one of the above tags
366 // if this is the <general> tag output the stuff that goes in the
368 if (strstr(line, "<general>"))
370 fout<< " <projectmanagement>KDevCustomProject</projectmanagement>\n";
371 fout<< " <projectdirectory>" <<projectDir.c_str()
372 << "</projectdirectory>\n"; //this one is important
373 fout<<" <absoluteprojectpath>true</absoluteprojectpath>\n";
376 // inside kdevcustomproject the <filelistdirectory> must be put
377 if (strstr(line, "<kdevcustomproject>"))
379 fout<<" <filelistdirectory>"<<outputDir.c_str()
380 <<"</filelistdirectory>\n";
382 // buildtool and builddir go inside <build>
383 if (strstr(line, "<build>"))
385 fout<<" <buildtool>make</buildtool>\n";
386 fout<<" <builddir>"<<outputDir.c_str()<<"</builddir>\n";
391 void cmGlobalKdevelopGenerator
392 ::CreateNewProjectFile(const std::string& outputDir,
393 const std::string& projectDir,
394 const std::string& filename,
395 const std::string& executable,
396 const std::string& cmakeFilePattern,
397 const std::string& fileToOpen,
398 const std::string& sessionFilename)
400 cmGeneratedFileStream fout(filename.c_str());
406 // check for a version control system
407 bool hasSvn = cmSystemTools::FileExists((projectDir + "/.svn").c_str());
408 bool hasCvs = cmSystemTools::FileExists((projectDir + "/CVS").c_str());
410 bool enableCxx = (this->GlobalGenerator->GetLanguageEnabled("C")
411 || this->GlobalGenerator->GetLanguageEnabled("CXX"));
412 bool enableFortran = this->GlobalGenerator->GetLanguageEnabled("Fortran");
413 std::string primaryLanguage = "C++";
414 if (enableFortran && !enableCxx)
416 primaryLanguage="Fortran77";
419 fout<<"<?xml version = '1.0'?>\n"
422 " <author></author>\n"
424 " <version>$VERSION$</version>\n"
425 " <projectmanagement>KDevCustomProject</projectmanagement>\n"
426 " <primarylanguage>" << primaryLanguage << "</primarylanguage>\n"
428 " <projectdirectory>" << projectDir.c_str() <<
429 "</projectdirectory>\n"; //this one is important
430 fout<<" <absoluteprojectpath>true</absoluteprojectpath>\n"; //and this one
432 // setup additional languages
433 fout<<" <secondaryLanguages>\n";
434 if (enableFortran && enableCxx)
436 fout<<" <language>Fortran</language>\n";
440 fout<<" <language>C</language>\n";
442 fout<<" </secondaryLanguages>\n";
446 fout << " <versioncontrol>kdevsubversion</versioncontrol>\n";
450 fout << " <versioncontrol>kdevcvsservice</versioncontrol>\n";
453 fout<<" </general>\n"
454 " <kdevcustomproject>\n"
455 " <filelistdirectory>" << outputDir.c_str() <<
456 "</filelistdirectory>\n"
458 " <mainprogram>" << executable.c_str() << "</mainprogram>\n"
459 " <directoryradio>custom</directoryradio>\n"
460 " <customdirectory>"<<outputDir.c_str()<<"</customdirectory>\n"
461 " <programargs></programargs>\n"
462 " <terminal>false</terminal>\n"
463 " <autocompile>true</autocompile>\n"
467 " <buildtool>make</buildtool>\n"; //this one is important
468 fout<<" <builddir>"<<outputDir.c_str()<<"</builddir>\n"; //and this one
471 " <abortonerror>false</abortonerror>\n"
472 " <numberofjobs>1</numberofjobs>\n"
473 " <dontact>false</dontact>\n"
474 " <makebin>" << this->GlobalGenerator->GetLocalGenerators()[0]->
475 GetMakefile()->GetRequiredDefinition("CMAKE_BUILD_TOOL")
477 " <selectedenvironment>default</selectedenvironment>\n"
480 " <envvar value=\"1\" name=\"VERBOSE\" />\n"
481 " <envvar value=\"1\" name=\"CMAKE_NO_VERBOSE\" />\n"
486 fout<<" <blacklist>\n";
487 for(std::vector<std::string>::const_iterator dirIt=this->Blacklist.begin();
488 dirIt != this->Blacklist.end();
491 fout<<" <path>" << dirIt->c_str() << "</path>\n";
493 fout<<" </blacklist>\n";
495 fout<<" </kdevcustomproject>\n"
496 " <kdevfilecreate>\n"
498 " <useglobaltypes>\n"
499 " <type ext=\"ui\" />\n"
500 " <type ext=\"cpp\" />\n"
501 " <type ext=\"h\" />\n"
502 " </useglobaltypes>\n"
503 " </kdevfilecreate>\n"
504 " <kdevdoctreeview>\n"
506 " <userdocDir>html/</userdocDir>\n"
507 " <apidocDir>html/</apidocDir>\n"
510 " <ignoredoxygen/>\n"
513 " <ignoredevhelp/>\n"
514 " </kdevdoctreeview>\n";
518 fout<<" <cppsupportpart>\n"
520 " <interfacesuffix>.h</interfacesuffix>\n"
521 " <implementationsuffix>.cpp</implementationsuffix>\n"
522 " </filetemplates>\n"
523 " </cppsupportpart>\n"
524 " <kdevcppsupport>\n"
525 " <codecompletion>\n"
526 " <includeGlobalFunctions>true</includeGlobalFunctions>\n"
527 " <includeTypes>true</includeTypes>\n"
528 " <includeEnums>true</includeEnums>\n"
529 " <includeTypedefs>false</includeTypedefs>\n"
530 " <automaticCodeCompletion>true</automaticCodeCompletion>\n"
531 " <automaticArgumentsHint>true</automaticArgumentsHint>\n"
532 " <automaticHeaderCompletion>true</automaticHeaderCompletion>\n"
533 " <codeCompletionDelay>250</codeCompletionDelay>\n"
534 " <argumentsHintDelay>400</argumentsHintDelay>\n"
535 " <headerCompletionDelay>250</headerCompletionDelay>\n"
536 " </codecompletion>\n"
538 " </kdevcppsupport>\n";
543 fout<<" <kdevfortransupport>\n"
545 " <division>false</division>\n"
546 " <extern>false</extern>\n"
547 " <declare>false</declare>\n"
548 " <pure>false</pure>\n"
549 " <argumentsall>false</argumentsall>\n"
550 " <commonall>false</commonall>\n"
551 " <truncationall>false</truncationall>\n"
552 " <usageall>false</usageall>\n"
553 " <f77all>false</f77all>\n"
554 " <portabilityall>false</portabilityall>\n"
555 " <argumentsonly/>\n"
557 " <truncationonly/>\n"
560 " <portabilityonly/>\n"
562 " </kdevfortransupport>\n";
565 // set up file groups. maybe this can be used with the CMake SOURCE_GROUP()
567 fout<<" <kdevfileview>\n"
569 " <group pattern=\"" << cmakeFilePattern.c_str() <<
570 "\" name=\"CMake\" />\n";
574 fout<<" <group pattern=\"*.h;*.hxx;*.hpp\" name=\"Header\" />\n"
575 " <group pattern=\"*.c\" name=\"C Sources\" />\n"
576 " <group pattern=\"*.cpp;*.C;*.cxx;*.cc\" name=\"C++ Sources\""
582 fout<<" <group pattern=\"*.f;*.F;*.f77;*.F77;*.f90;*.F90;*.for;*.f95;"
583 "*.F95\" name=\"Fortran Sources\" />\n";
586 fout<<" <group pattern=\"*.ui\" name=\"Qt Designer files\" />\n"
587 " <hidenonprojectfiles>true</hidenonprojectfiles>\n"
590 " <hidepatterns>*.o,*.lo,CVS,*~,cmake*</hidepatterns>\n"
591 " <hidenonprojectfiles>true</hidenonprojectfiles>\n"
596 if (sessionFilename.empty())
601 // and a session file, so that kdevelop opens a file if it opens the
602 // project the first time
603 cmGeneratedFileStream devses(sessionFilename.c_str());
608 devses<<"<?xml version = '1.0' encoding = \'UTF-8\'?>\n"
609 "<!DOCTYPE KDevPrjSession>\n"
611 " <DocsAndViews NumberOfDocuments=\"1\" >\n"
612 " <Doc0 NumberOfViews=\"1\" URL=\"file://" << fileToOpen.c_str() <<
614 " <View0 line=\"0\" Type=\"Source\" />\n"
617 "</KDevPrjSession>\n";