packaging: Initial packaging
[platform/upstream/cmake.git] / Source / cmExtraSublimeTextGenerator.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 "cmExtraSublimeTextGenerator.h"
14 #include "cmake.h"
15 #include "cmGeneratedFileStream.h"
16 #include "cmGeneratorTarget.h"
17 #include "cmGlobalUnixMakefileGenerator3.h"
18 #include "cmLocalGenerator.h"
19 #include "cmLocalUnixMakefileGenerator3.h"
20 #include "cmMakefile.h"
21 #include "cmSourceFile.h"
22 #include "cmSystemTools.h"
23 #include "cmTarget.h"
24 #include "cmXMLSafe.h"
25
26 #include <cmsys/SystemTools.hxx>
27
28 /*
29 Sublime Text 2 Generator
30 Author: MornĂ© Chamberlain
31 This generator was initially based off of the CodeBlocks generator.
32
33 Some useful URLs:
34 Homepage:
35 http://www.sublimetext.com/
36
37 File format docs:
38 http://www.sublimetext.com/docs/2/projects.html
39 http://sublimetext.info/docs/en/reference/build_systems.html
40 */
41
42 //----------------------------------------------------------------------------
43 void cmExtraSublimeTextGenerator
44 ::GetDocumentation(cmDocumentationEntry& entry, const char*) const
45 {
46   entry.Name = this->GetName();
47   entry.Brief = "Generates Sublime Text 2 project files.";
48   entry.Full =
49     "Project files for Sublime Text 2 will be created in the top directory "
50     "and in every subdirectory which features a CMakeLists.txt file "
51     "containing a PROJECT() call. "
52     "Additionally Makefiles (or build.ninja files) are generated into the "
53     "build tree.  The appropriate make program can build the project through "
54     "the default make target.  A \"make install\" target is also provided.";
55 }
56
57 cmExtraSublimeTextGenerator::cmExtraSublimeTextGenerator()
58 :cmExternalMakefileProjectGenerator()
59 {
60 #if defined(_WIN32)
61   this->SupportedGlobalGenerators.push_back("MinGW Makefiles");
62   this->SupportedGlobalGenerators.push_back("NMake Makefiles");
63 // disable until somebody actually tests it:
64 //  this->SupportedGlobalGenerators.push_back("MSYS Makefiles");
65 #endif
66   this->SupportedGlobalGenerators.push_back("Ninja");
67   this->SupportedGlobalGenerators.push_back("Unix Makefiles");
68 }
69
70
71 void cmExtraSublimeTextGenerator::Generate()
72 {
73   // for each sub project in the project create a sublime text 2 project
74   for (std::map<cmStdString, std::vector<cmLocalGenerator*> >::const_iterator
75        it = this->GlobalGenerator->GetProjectMap().begin();
76       it!= this->GlobalGenerator->GetProjectMap().end();
77       ++it)
78     {
79     // create a project file
80     this->CreateProjectFile(it->second);
81     }
82 }
83
84
85 void cmExtraSublimeTextGenerator::CreateProjectFile(
86                                      const std::vector<cmLocalGenerator*>& lgs)
87 {
88   const cmMakefile* mf=lgs[0]->GetMakefile();
89   std::string outputDir=mf->GetStartOutputDirectory();
90   std::string projectName=mf->GetProjectName();
91
92   const std::string filename =
93                      outputDir + "/" + projectName + ".sublime-project";
94
95   this->CreateNewProjectFile(lgs, filename);
96 }
97
98 void cmExtraSublimeTextGenerator
99   ::CreateNewProjectFile(const std::vector<cmLocalGenerator*>& lgs,
100                          const std::string& filename)
101 {
102   const cmMakefile* mf=lgs[0]->GetMakefile();
103   cmGeneratedFileStream fout(filename.c_str());
104   if(!fout)
105     {
106     return;
107     }
108
109   const std::string &sourceRootRelativeToOutput = cmSystemTools::RelativePath(
110                      mf->GetHomeOutputDirectory(),
111                      mf->GetHomeDirectory());
112   // Write the folder entries to the project file
113   fout << "{\n";
114   fout << "\t\"folders\":\n\t[\n\t";
115   if (!sourceRootRelativeToOutput.empty())
116     {
117       fout << "\t{\n\t\t\t\"path\": \"" << sourceRootRelativeToOutput << "\"";
118       const std::string &outputRelativeToSourceRoot =
119         cmSystemTools::RelativePath(mf->GetHomeDirectory(),
120                                     mf->GetHomeOutputDirectory());
121       if ((!outputRelativeToSourceRoot.empty()) &&
122         ((outputRelativeToSourceRoot.length() < 3) ||
123           (outputRelativeToSourceRoot.substr(0, 3) != "../")))
124         {
125         fout << ",\n\t\t\t\"folder_exclude_patterns\": [\"" <<
126                 outputRelativeToSourceRoot << "\"]";
127         }
128     }
129   else
130     {
131       fout << "\t{\n\t\t\t\"path\": \"./\"";
132     }
133   fout << "\n\t\t}";
134   // End of the folders section
135   fout << "\n\t]";
136
137   // Write the beginning of the build systems section to the project file
138   fout << ",\n\t\"build_systems\":\n\t[\n\t";
139
140   // Set of include directories over all targets (sublime text/sublimeclang
141   // doesn't currently support these settings per build system, only project
142   // wide
143   MapSourceFileFlags sourceFileFlags;
144   AppendAllTargets(lgs, mf, fout, sourceFileFlags);
145
146   // End of build_systems
147   fout << "\n\t]";
148   fout << "\n\t}";
149 }
150
151
152 void cmExtraSublimeTextGenerator::
153   AppendAllTargets(const std::vector<cmLocalGenerator*>& lgs,
154                    const cmMakefile* mf,
155                    cmGeneratedFileStream& fout,
156                    MapSourceFileFlags& sourceFileFlags)
157 {
158   std::string make = mf->GetRequiredDefinition("CMAKE_MAKE_PROGRAM");
159   std::string compiler = "";
160   if (!lgs.empty())
161     {
162       this->AppendTarget(fout, "all", lgs[0], 0, make.c_str(), mf,
163                          compiler.c_str(), sourceFileFlags, true);
164       this->AppendTarget(fout, "clean", lgs[0], 0, make.c_str(), mf,
165                          compiler.c_str(), sourceFileFlags, false);
166     }
167
168   // add all executable and library targets and some of the GLOBAL
169   // and UTILITY targets
170   for (std::vector<cmLocalGenerator*>::const_iterator lg=lgs.begin();
171        lg!=lgs.end(); lg++)
172     {
173     cmMakefile* makefile=(*lg)->GetMakefile();
174     cmTargets& targets=makefile->GetTargets();
175     for (cmTargets::iterator ti = targets.begin();
176          ti != targets.end(); ti++)
177       {
178       switch(ti->second.GetType())
179         {
180         case cmTarget::GLOBAL_TARGET:
181           {
182           bool insertTarget = false;
183           // Only add the global targets from CMAKE_BINARY_DIR,
184           // not from the subdirs
185           if (strcmp(makefile->GetStartOutputDirectory(),
186                      makefile->GetHomeOutputDirectory())==0)
187             {
188             insertTarget = true;
189             // only add the "edit_cache" target if it's not ccmake, because
190             // this will not work within the IDE
191             if (ti->first == "edit_cache")
192               {
193               const char* editCommand = makefile->GetDefinition
194                                                         ("CMAKE_EDIT_COMMAND");
195               if (editCommand == 0)
196                 {
197                 insertTarget = false;
198                 }
199               else if (strstr(editCommand, "ccmake")!=NULL)
200                 {
201                 insertTarget = false;
202                 }
203               }
204             }
205           if (insertTarget)
206             {
207             this->AppendTarget(fout, ti->first.c_str(), *lg, 0,
208                                make.c_str(), makefile, compiler.c_str(),
209                                sourceFileFlags, false);
210             }
211           }
212           break;
213         case cmTarget::UTILITY:
214           // Add all utility targets, except the Nightly/Continuous/
215           // Experimental-"sub"targets as e.g. NightlyStart
216           if (((ti->first.find("Nightly")==0)   &&(ti->first!="Nightly"))
217              || ((ti->first.find("Continuous")==0)&&(ti->first!="Continuous"))
218              || ((ti->first.find("Experimental")==0)
219                                                && (ti->first!="Experimental")))
220             {
221             break;
222             }
223
224           this->AppendTarget(fout, ti->first.c_str(), *lg, 0,
225                              make.c_str(), makefile, compiler.c_str(),
226                              sourceFileFlags, false);
227           break;
228         case cmTarget::EXECUTABLE:
229         case cmTarget::STATIC_LIBRARY:
230         case cmTarget::SHARED_LIBRARY:
231         case cmTarget::MODULE_LIBRARY:
232         case cmTarget::OBJECT_LIBRARY:
233           {
234           this->AppendTarget(fout, ti->first.c_str(), *lg, &ti->second,
235                              make.c_str(), makefile, compiler.c_str(),
236                              sourceFileFlags, false);
237           std::string fastTarget = ti->first;
238           fastTarget += "/fast";
239           this->AppendTarget(fout, fastTarget.c_str(), *lg, &ti->second,
240                              make.c_str(), makefile, compiler.c_str(),
241                              sourceFileFlags, false);
242           }
243           break;
244         default:
245           break;
246         }
247       }
248     }
249 }
250
251 void cmExtraSublimeTextGenerator::
252   AppendTarget(cmGeneratedFileStream& fout,
253                const char* targetName,
254                cmLocalGenerator* lg,
255                cmTarget* target,
256                const char* make,
257                const cmMakefile* makefile,
258                const char*, //compiler
259                MapSourceFileFlags& sourceFileFlags,
260                bool firstTarget)
261 {
262
263   if (target != 0)
264     {
265       cmGeneratorTarget *gtgt = this->GlobalGenerator
266                                     ->GetGeneratorTarget(target);
267       std::vector<cmSourceFile*> const& sourceFiles = target->GetSourceFiles();
268       std::vector<cmSourceFile*>::const_iterator sourceFilesEnd =
269         sourceFiles.end();
270       for (std::vector<cmSourceFile*>::const_iterator iter =
271         sourceFiles.begin(); iter != sourceFilesEnd; ++iter)
272         {
273           cmSourceFile* sourceFile = *iter;
274           MapSourceFileFlags::iterator sourceFileFlagsIter =
275             sourceFileFlags.find(sourceFile->GetFullPath());
276           if (sourceFileFlagsIter == sourceFileFlags.end())
277             {
278             sourceFileFlagsIter =
279               sourceFileFlags.insert(MapSourceFileFlags::value_type(
280                 sourceFile->GetFullPath(), std::vector<std::string>())).first;
281             }
282           std::vector<std::string>& flags = sourceFileFlagsIter->second;
283           std::string flagsString =
284             this->ComputeFlagsForObject(*iter, lg, target, gtgt);
285           std::string definesString =
286             this->ComputeDefines(*iter, lg, target, gtgt);
287           flags.clear();
288           cmsys::RegularExpression flagRegex;
289           // Regular expression to extract compiler flags from a string
290           // https://gist.github.com/3944250
291           const char* regexString =
292             "(^|[ ])-[DIOUWfgs][^= ]+(=\\\"[^\"]+\\\"|=[^\"][^ ]+)?";
293           flagRegex.compile(regexString);
294           std::string workString = flagsString + " " + definesString;
295           while (flagRegex.find(workString))
296             {
297               std::string::size_type start = flagRegex.start();
298               if (workString[start] == ' ')
299                 {
300                   start++;
301                 }
302               flags.push_back(workString.substr(start,
303                 flagRegex.end() - start));
304               if (flagRegex.end() < workString.size())
305                 {
306                 workString = workString.substr(flagRegex.end());
307                 }
308                 else
309                 {
310                 workString = "";
311                 }
312             }
313         }
314     }
315
316   // Ninja uses ninja.build files (look for a way to get the output file name
317   // from cmMakefile or something)
318   std::string makefileName;
319   if (strcmp(this->GlobalGenerator->GetName(), "Ninja")==0)
320     {
321       makefileName = "build.ninja";
322     }
323     else
324     {
325       makefileName = "Makefile";
326     }
327   if (!firstTarget)
328     {
329     fout << ",\n\t";
330     }
331   fout << "\t{\n\t\t\t\"name\": \"" << makefile->GetProjectName() << " - " <<
332           targetName << "\",\n";
333   fout << "\t\t\t\"cmd\": [" <<
334           this->BuildMakeCommand(make, makefileName.c_str(), targetName) <<
335           "],\n";
336   fout << "\t\t\t\"working_dir\": \"${project_path}\",\n";
337   fout << "\t\t\t\"file_regex\": \"^(..[^:]*):([0-9]+):?([0-9]+)?:? (.*)$\"\n";
338   fout << "\t\t}";
339 }
340
341 // Create the command line for building the given target using the selected
342 // make
343 std::string cmExtraSublimeTextGenerator::BuildMakeCommand(
344              const std::string& make, const char* makefile, const char* target)
345 {
346   std::string command = "\"";
347   command += make + "\"";
348   if (strcmp(this->GlobalGenerator->GetName(), "NMake Makefiles")==0)
349     {
350     std::string makefileName = cmSystemTools::ConvertToOutputPath(makefile);
351     command += ", \"/NOLOGO\", \"/f\", \"";
352     command += makefileName + "\"";
353     command += ", \"VERBOSE=1\", \"";
354     command += target;
355     command += "\"";
356     }
357   else if (strcmp(this->GlobalGenerator->GetName(), "Ninja")==0)
358     {
359     std::string makefileName = cmSystemTools::ConvertToOutputPath(makefile);
360     command += ", \"-f\", \"";
361     command += makefileName + "\"";
362     command += ", \"-v\", \"";
363     command += target;
364     command += "\"";
365     }
366   else
367     {
368     std::string makefileName;
369     if (strcmp(this->GlobalGenerator->GetName(), "MinGW Makefiles")==0)
370       {
371         // no escaping of spaces in this case, see
372         // http://public.kitware.com/Bug/view.php?id=10014
373         makefileName = makefile;
374       }
375       else
376       {
377         makefileName = cmSystemTools::ConvertToOutputPath(makefile);
378       }
379     command += ", \"-f\", \"";
380     command += makefileName + "\"";
381     command += ", \"VERBOSE=1\", \"";
382     command += target;
383     command += "\"";
384     }
385   return command;
386 }
387
388 // TODO: Most of the code is picked up from the Ninja generator, refactor it.
389 std::string
390 cmExtraSublimeTextGenerator::ComputeFlagsForObject(cmSourceFile* source,
391                                                    cmLocalGenerator* lg,
392                                                    cmTarget *target,
393                                                    cmGeneratorTarget* gtgt)
394 {
395   std::string flags;
396
397   cmMakefile *makefile = lg->GetMakefile();
398   const char* language = source->GetLanguage();
399   if (language == NULL)
400    {
401    language = "C";
402    }
403   const char* config = makefile->GetSafeDefinition("CMAKE_BUILD_TYPE");
404   // Add language-specific flags.
405   lg->AddLanguageFlags(flags, language, config);
406
407   lg->AddArchitectureFlags(flags, gtgt, language, config);
408
409   // TODO: Fortran support.
410   // // Fortran-specific flags computed for this target.
411   // if(*l == "Fortran")
412   //   {
413   //   this->AddFortranFlags(flags);
414   //   }
415
416   // Add shared-library flags if needed.
417   lg->AddCMP0018Flags(flags, target, language, config);
418
419   // Add include directory flags.
420   {
421   std::vector<std::string> includes;
422   lg->GetIncludeDirectories(includes, gtgt, language, config);
423   std::string includeFlags =
424     lg->GetIncludeFlags(includes, gtgt, language, true); // full include paths
425   lg->AppendFlags(flags, includeFlags.c_str());
426   }
427
428   // Append old-style preprocessor definition flags.
429   lg->AppendFlags(flags, makefile->GetDefineFlags());
430
431   // Add target-specific flags.
432   lg->AddCompileOptions(flags, target, config, language);
433
434   // Add source file specific flags.
435   lg->AppendFlags(flags, source->GetProperty("COMPILE_FLAGS"));
436
437   // TODO: Handle Apple frameworks.
438
439   return flags;
440 }
441
442 // TODO: Refactor with
443 // void cmMakefileTargetGenerator::WriteTargetLanguageFlags().
444 std::string
445 cmExtraSublimeTextGenerator::
446 ComputeDefines(cmSourceFile *source, cmLocalGenerator* lg, cmTarget *target,
447                cmGeneratorTarget*)
448
449 {
450   std::set<std::string> defines;
451   cmMakefile *makefile = lg->GetMakefile();
452   const char* language = source->GetLanguage();
453   if (language == NULL)
454    {
455    language = "";
456    }
457   const char* config = makefile->GetSafeDefinition("CMAKE_BUILD_TYPE");
458
459   // Add the export symbol definition for shared library objects.
460   if(const char* exportMacro = target->GetExportMacro())
461     {
462     lg->AppendDefines(defines, exportMacro);
463     }
464
465   // Add preprocessor definitions for this target and configuration.
466   lg->AddCompileDefinitions(defines, target, config);
467   lg->AppendDefines(defines, source->GetProperty("COMPILE_DEFINITIONS"));
468   {
469   std::string defPropName = "COMPILE_DEFINITIONS_";
470   defPropName += cmSystemTools::UpperCase(config);
471   lg->AppendDefines(defines, source->GetProperty(defPropName.c_str()));
472   }
473
474   std::string definesString;
475   lg->JoinDefines(defines, definesString, language);
476
477   return definesString;
478 }