packaging: Initial packaging
[platform/upstream/cmake.git] / Source / cmGlobalKdevelopGenerator.cxx
1 /*============================================================================
2   CMake - Cross Platform Makefile Generator
3   Copyright 2004-2009 Kitware, Inc.
4   Copyright 2004 Alexander Neundorf (neundorf@kde.org)
5
6   Distributed under the OSI-approved BSD License (the "License");
7   see accompanying file Copyright.txt for details.
8
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"
17 #include "cmake.h"
18 #include "cmSourceFile.h"
19 #include "cmGeneratedFileStream.h"
20 #include "cmSystemTools.h"
21
22 #include <cmsys/SystemTools.hxx>
23 #include <cmsys/Directory.hxx>
24
25 //----------------------------------------------------------------------------
26 void cmGlobalKdevelopGenerator
27 ::GetDocumentation(cmDocumentationEntry& entry, const char*) const
28 {
29   entry.Name = this->GetName();
30   entry.Brief = "Generates KDevelop 3 project files.";
31   entry.Full =
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 "
38     "build tree.  Any "
39     "standard UNIX-style make program can build the project through the "
40     "default make target.  A \"make install\" target is also provided.";
41 }
42
43 cmGlobalKdevelopGenerator::cmGlobalKdevelopGenerator()
44 :cmExternalMakefileProjectGenerator()
45 {
46   this->SupportedGlobalGenerators.push_back("Unix Makefiles");
47 #ifdef CMAKE_USE_NINJA
48   this->SupportedGlobalGenerators.push_back("Ninja");
49 #endif
50 }
51
52 void cmGlobalKdevelopGenerator::Generate()
53 {
54   // for each sub project in the project create
55   // a kdevelop project
56   for (std::map<cmStdString, std::vector<cmLocalGenerator*> >::const_iterator
57        it = this->GlobalGenerator->GetProjectMap().begin();
58       it!= this->GlobalGenerator->GetProjectMap().end();
59       ++it)
60     {
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))
71       {
72       cmSystemTools::Error("Can not create filelist file");
73       return;
74       }
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();
79          lg!=lgs.end(); lg++)
80       {
81       cmMakefile* makefile=(*lg)->GetMakefile();
82       cmTargets& targets=makefile->GetTargets();
83       for (cmTargets::iterator ti = targets.begin();
84            ti != targets.end(); ti++)
85         {
86         if (ti->second.GetType()==cmTarget::EXECUTABLE)
87           {
88           executable = ti->second.GetProperty("LOCATION");
89           break;
90           }
91         }
92       if (!executable.empty())
93         {
94         break;
95         }
96       }
97
98     // now create a project file
99     this->CreateProjectFile(outputDir, projectDir, projectName,
100                             executable, cmakeFilePattern, fileToOpen);
101     }
102 }
103
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)
111 {
112   std::string projectDir = projectDirIn + "/";
113   std::string filename = outputDir+ "/" + projectname +".kdevelop.filelist";
114
115   std::set<cmStdString> files;
116   std::string tmp;
117
118   for (std::vector<cmLocalGenerator*>::const_iterator it=lgs.begin();
119        it!=lgs.end(); it++)
120     {
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++)
125       {
126       tmp=*lt;
127       cmSystemTools::ReplaceString(tmp, projectDir.c_str(), "");
128       // make sure the file is part of this source tree
129       if ((tmp[0]!='/') &&
130           (strstr(tmp.c_str(),
131                   cmake::GetCMakeFilesDirectoryPostSlash())==0))
132         {
133         files.insert(tmp);
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))
139           {
140           cmakeFilePattern+=tmp+";";
141           }
142         }
143       }
144
145     //get all sources
146     cmTargets& targets=makefile->GetTargets();
147     for (cmTargets::iterator ti = targets.begin();
148          ti != targets.end(); ti++)
149       {
150       const std::vector<cmSourceFile*>& sources=ti->second.GetSourceFiles();
151       for (std::vector<cmSourceFile*>::const_iterator si=sources.begin();
152            si!=sources.end(); si++)
153         {
154         tmp=(*si)->GetFullPath();
155         std::string headerBasename=cmSystemTools::GetFilenamePath(tmp);
156         headerBasename+="/";
157         headerBasename+=cmSystemTools::GetFilenameWithoutExtension(tmp);
158
159         cmSystemTools::ReplaceString(tmp, projectDir.c_str(), "");
160
161         if ((tmp[0]!='/')  &&
162             (strstr(tmp.c_str(),
163                   cmake::GetCMakeFilesDirectoryPostSlash())==0) &&
164            (cmSystemTools::GetFilenameExtension(tmp)!=".moc"))
165           {
166           files.insert(tmp);
167
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)
172             {
173             std::string hname=headerBasename;
174             hname += ".";
175             hname += *ext;
176             if(cmSystemTools::FileExists(hname.c_str()))
177               {
178               cmSystemTools::ReplaceString(hname, projectDir.c_str(), "");
179               files.insert(hname);
180               break;
181               }
182             }
183           }
184         }
185       for (std::vector<std::string>::const_iterator lt=listFiles.begin();
186            lt!=listFiles.end(); lt++)
187         {
188         tmp=*lt;
189         cmSystemTools::ReplaceString(tmp, projectDir.c_str(), "");
190         if ((tmp[0]!='/') &&
191             (strstr(tmp.c_str(),
192                     cmake::GetCMakeFilesDirectoryPostSlash())==0))
193           {
194           files.insert(tmp.c_str());
195           }
196         }
197       }
198     }
199
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());
203   if (oldFilelist)
204     {
205     while (cmSystemTools::GetLineFromStream(oldFilelist, tmp))
206       {
207       if (tmp[0]=='/')
208         {
209         continue;
210         }
211       std::string completePath=projectDir+tmp;
212       if (cmSystemTools::FileExists(completePath.c_str()))
213         {
214         files.insert(tmp);
215         }
216       }
217     oldFilelist.close();
218     }
219
220   //now write the new filename
221   cmGeneratedFileStream fout(filename.c_str());
222   if(!fout)
223     {
224     return false;
225     }
226
227   fileToOpen="";
228   for (std::set<cmStdString>::const_iterator it=files.begin();
229        it!=files.end(); it++)
230     {
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())
235     {
236        std::string ext = cmSystemTools::GetFilenameExtension(tmp);
237        if ((ext==".c") || (ext==".cc") || (ext==".cpp")  || (ext==".cxx")
238            || (ext==".C") || (ext==".h") || (ext==".hpp"))
239        {
240        fileToOpen=tmp;
241        }
242     }
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] != '/')
247       {
248       fout << tmp.c_str() <<"\n";
249       }
250     }
251   return true;
252 }
253
254
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)
264 {
265   this->Blacklist.clear();
266
267   std::string filename=outputDir+"/";
268   filename+=projectname+".kdevelop";
269   std::string sessionFilename=outputDir+"/";
270   sessionFilename+=projectname+".kdevses";
271
272   if (cmSystemTools::FileExists(filename.c_str()))
273     {
274     this->MergeProjectFiles(outputDir, projectDir, filename,
275                             executable, cmakeFilePattern,
276                             fileToOpen, sessionFilename);
277     }
278   else
279     {
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
283     cmsys::Directory d;
284     if (d.Load(projectDir.c_str()))
285       {
286       size_t numf = d.GetNumberOfFiles();
287       for (unsigned int i = 0; i < numf; i++)
288         {
289         std::string nextFile = d.GetFile(i);
290         if ((nextFile!=".") && (nextFile!=".."))
291           {
292           std::string tmp = projectDir;
293           tmp += "/";
294           tmp += nextFile;
295           if (cmSystemTools::FileIsDirectory(tmp.c_str()))
296             {
297             tmp += "/CMakeCache.txt";
298             if ((nextFile == "CMakeFiles")
299                 || (cmSystemTools::FileExists(tmp.c_str())))
300               {
301               this->Blacklist.push_back(nextFile);
302               }
303             }
304           }
305         }
306       }
307     this->CreateNewProjectFile(outputDir, projectDir, filename,
308                                executable, cmakeFilePattern,
309                                fileToOpen, sessionFilename);
310     }
311
312 }
313
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)
322 {
323   std::ifstream oldProjectFile(filename.c_str());
324   if (!oldProjectFile)
325     {
326     this->CreateNewProjectFile(outputDir, projectDir, filename,
327                                executable, cmakeFilePattern,
328                                fileToOpen, sessionFilename);
329     return;
330     }
331
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 */
335   std::string tmp;
336   std::vector<std::string> lines;
337   while (cmSystemTools::GetLineFromStream(oldProjectFile, tmp))
338     {
339     lines.push_back(tmp);
340     }
341   oldProjectFile.close();
342
343   cmGeneratedFileStream fout(filename.c_str());
344   if(!fout)
345     {
346     return;
347     }
348
349   for (std::vector<std::string>::const_iterator it=lines.begin();
350        it!=lines.end(); it++)
351     {
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))
360       {
361       continue;
362       }
363
364     // output the line from the file if it is not one of the above tags
365     fout<<*it<<"\n";
366     // if this is the <general> tag output the stuff that goes in the
367     // general tag
368     if (strstr(line, "<general>"))
369       {
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";
374       //and this one
375       }
376     // inside kdevcustomproject the <filelistdirectory> must be put
377     if (strstr(line, "<kdevcustomproject>"))
378       {
379       fout<<"    <filelistdirectory>"<<outputDir.c_str()
380           <<"</filelistdirectory>\n";
381       }
382     // buildtool and builddir go inside <build>
383     if (strstr(line, "<build>"))
384       {
385       fout<<"      <buildtool>make</buildtool>\n";
386       fout<<"      <builddir>"<<outputDir.c_str()<<"</builddir>\n";
387       }
388     }
389 }
390
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)
399 {
400   cmGeneratedFileStream fout(filename.c_str());
401   if(!fout)
402     {
403     return;
404     }
405
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());
409
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)
415     {
416     primaryLanguage="Fortran77";
417     }
418
419   fout<<"<?xml version = '1.0'?>\n"
420         "<kdevelop>\n"
421         "  <general>\n"
422         "  <author></author>\n"
423         "  <email></email>\n"
424         "  <version>$VERSION$</version>\n"
425         "  <projectmanagement>KDevCustomProject</projectmanagement>\n"
426         "  <primarylanguage>" << primaryLanguage << "</primarylanguage>\n"
427         "  <ignoreparts/>\n"
428         "  <projectdirectory>" << projectDir.c_str() <<
429         "</projectdirectory>\n";   //this one is important
430   fout<<"  <absoluteprojectpath>true</absoluteprojectpath>\n"; //and this one
431
432   // setup additional languages
433   fout<<"  <secondaryLanguages>\n";
434   if (enableFortran && enableCxx)
435     {
436     fout<<"     <language>Fortran</language>\n";
437     }
438   if (enableCxx)
439     {
440     fout<<"     <language>C</language>\n";
441     }
442   fout<<"  </secondaryLanguages>\n";
443
444   if (hasSvn)
445     {
446     fout << "  <versioncontrol>kdevsubversion</versioncontrol>\n";
447     }
448   else if (hasCvs)
449     {
450     fout << "  <versioncontrol>kdevcvsservice</versioncontrol>\n";
451     }
452
453   fout<<"  </general>\n"
454         "  <kdevcustomproject>\n"
455         "    <filelistdirectory>" << outputDir.c_str() <<
456         "</filelistdirectory>\n"
457         "    <run>\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"
464         "      <envvars/>\n"
465         "    </run>\n"
466         "    <build>\n"
467         "      <buildtool>make</buildtool>\n"; //this one is important
468   fout<<"      <builddir>"<<outputDir.c_str()<<"</builddir>\n";  //and this one
469   fout<<"    </build>\n"
470         "    <make>\n"
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")
476             << " </makebin>\n"
477         "      <selectedenvironment>default</selectedenvironment>\n"
478         "      <environments>\n"
479         "        <default>\n"
480         "          <envvar value=\"1\" name=\"VERBOSE\" />\n"
481         "          <envvar value=\"1\" name=\"CMAKE_NO_VERBOSE\" />\n"
482         "        </default>\n"
483         "      </environments>\n"
484         "    </make>\n";
485
486   fout<<"    <blacklist>\n";
487   for(std::vector<std::string>::const_iterator dirIt=this->Blacklist.begin();
488       dirIt != this->Blacklist.end();
489       ++dirIt)
490     {
491     fout<<"      <path>" << dirIt->c_str() << "</path>\n";
492     }
493   fout<<"    </blacklist>\n";
494
495   fout<<"  </kdevcustomproject>\n"
496         "  <kdevfilecreate>\n"
497         "    <filetypes/>\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"
505         "    <projectdoc>\n"
506         "      <userdocDir>html/</userdocDir>\n"
507         "      <apidocDir>html/</apidocDir>\n"
508         "    </projectdoc>\n"
509         "    <ignoreqt_xml/>\n"
510         "    <ignoredoxygen/>\n"
511         "    <ignorekdocs/>\n"
512         "    <ignoretocs/>\n"
513         "    <ignoredevhelp/>\n"
514         "  </kdevdoctreeview>\n";
515
516   if (enableCxx)
517     {
518     fout<<"  <cppsupportpart>\n"
519           "    <filetemplates>\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"
537           "    <references/>\n"
538           "  </kdevcppsupport>\n";
539     }
540
541   if (enableFortran)
542     {
543     fout<<"  <kdevfortransupport>\n"
544           "    <ftnchek>\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"
556           "      <commononly/>\n"
557           "      <truncationonly/>\n"
558           "      <usageonly/>\n"
559           "      <f77only/>\n"
560           "      <portabilityonly/>\n"
561           "    </ftnchek>\n"
562           "  </kdevfortransupport>\n";
563     }
564
565   // set up file groups. maybe this can be used with the CMake SOURCE_GROUP()
566   // command
567   fout<<"  <kdevfileview>\n"
568         "    <groups>\n"
569         "      <group pattern=\"" << cmakeFilePattern.c_str() <<
570         "\" name=\"CMake\" />\n";
571
572   if (enableCxx)
573     {
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\""
577           "/>\n";
578     }
579
580   if (enableFortran)
581     {
582     fout<<"      <group pattern=\"*.f;*.F;*.f77;*.F77;*.f90;*.F90;*.for;*.f95;"
583           "*.F95\" name=\"Fortran Sources\" />\n";
584     }
585
586   fout<<"      <group pattern=\"*.ui\" name=\"Qt Designer files\" />\n"
587         "      <hidenonprojectfiles>true</hidenonprojectfiles>\n"
588         "    </groups>\n"
589         "    <tree>\n"
590         "      <hidepatterns>*.o,*.lo,CVS,*~,cmake*</hidepatterns>\n"
591         "      <hidenonprojectfiles>true</hidenonprojectfiles>\n"
592         "    </tree>\n"
593         "  </kdevfileview>\n"
594         "</kdevelop>\n";
595
596   if (sessionFilename.empty())
597     {
598     return;
599     }
600
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());
604   if(!devses)
605     {
606     return;
607     }
608   devses<<"<?xml version = '1.0' encoding = \'UTF-8\'?>\n"
609           "<!DOCTYPE KDevPrjSession>\n"
610           "<KDevPrjSession>\n"
611           " <DocsAndViews NumberOfDocuments=\"1\" >\n"
612           "  <Doc0 NumberOfViews=\"1\" URL=\"file://" << fileToOpen.c_str() <<
613           "\" >\n"
614           "   <View0 line=\"0\" Type=\"Source\" />\n"
615           "  </Doc0>\n"
616           " </DocsAndViews>\n"
617           "</KDevPrjSession>\n";
618 }
619