1 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2    file Copyright.txt or https://cmake.org/licensing for details.  */
3 #include "cmExtraCodeBlocksGenerator.h"
4 
5 #include <map>
6 #include <memory>
7 #include <ostream>
8 #include <set>
9 #include <utility>
10 
11 #include <cmext/algorithm>
12 
13 #include "cmAlgorithms.h"
14 #include "cmGeneratedFileStream.h"
15 #include "cmGeneratorTarget.h"
16 #include "cmGlobalGenerator.h"
17 #include "cmLocalGenerator.h"
18 #include "cmMakefile.h"
19 #include "cmRange.h"
20 #include "cmSourceFile.h"
21 #include "cmStateTypes.h"
22 #include "cmStringAlgorithms.h"
23 #include "cmSystemTools.h"
24 #include "cmValue.h"
25 #include "cmXMLWriter.h"
26 #include "cmake.h"
27 
28 /* Some useful URLs:
29 Homepage:
30 http://www.codeblocks.org
31 
32 File format docs:
33 http://wiki.codeblocks.org/index.php?title=File_formats_description
34 http://wiki.codeblocks.org/index.php?title=Workspace_file
35 http://wiki.codeblocks.org/index.php?title=Project_file
36 
37 Discussion:
38 http://forums.codeblocks.org/index.php/topic,6789.0.html
39 */
40 
41 cmExtraCodeBlocksGenerator::cmExtraCodeBlocksGenerator() = default;
42 
43 cmExternalMakefileProjectGeneratorFactory*
GetFactory()44 cmExtraCodeBlocksGenerator::GetFactory()
45 {
46   static cmExternalMakefileProjectGeneratorSimpleFactory<
47     cmExtraCodeBlocksGenerator>
48     factory("CodeBlocks", "Generates CodeBlocks project files.");
49 
50   if (factory.GetSupportedGlobalGenerators().empty()) {
51 #if defined(_WIN32)
52     factory.AddSupportedGlobalGenerator("MinGW Makefiles");
53     factory.AddSupportedGlobalGenerator("NMake Makefiles");
54     factory.AddSupportedGlobalGenerator("NMake Makefiles JOM");
55 // disable until somebody actually tests it:
56 // this->AddSupportedGlobalGenerator("MSYS Makefiles");
57 #endif
58     factory.AddSupportedGlobalGenerator("Ninja");
59     factory.AddSupportedGlobalGenerator("Unix Makefiles");
60   }
61 
62   return &factory;
63 }
64 
Generate()65 void cmExtraCodeBlocksGenerator::Generate()
66 {
67   // for each sub project in the project create a codeblocks project
68   for (auto const& it : this->GlobalGenerator->GetProjectMap()) {
69     // create a project file
70     this->CreateProjectFile(it.second);
71   }
72 }
73 
74 /* create the project file */
CreateProjectFile(const std::vector<cmLocalGenerator * > & lgs)75 void cmExtraCodeBlocksGenerator::CreateProjectFile(
76   const std::vector<cmLocalGenerator*>& lgs)
77 {
78   std::string outputDir = lgs[0]->GetCurrentBinaryDirectory();
79   std::string projectName = lgs[0]->GetProjectName();
80 
81   std::string filename = cmStrCat(outputDir, '/', projectName, ".cbp");
82   std::string sessionFilename =
83     cmStrCat(outputDir, '/', projectName, ".layout");
84 
85   this->CreateNewProjectFile(lgs, filename);
86 }
87 
88 /* Tree is used to create a "Virtual Folder" in CodeBlocks, in which all
89  CMake files this project depends on will be put. This means additionally
90  to the "Sources" and "Headers" virtual folders of CodeBlocks, there will
91  now also be a "CMake Files" virtual folder.
92  Patch by Daniel Teske <daniel.teske AT nokia.com> (which use C::B project
93  files in QtCreator).*/
94 struct Tree
95 {
96   std::string path; // only one component of the path
97   std::vector<Tree> folders;
98   std::set<std::string> files;
99   void InsertPath(const std::vector<std::string>& split,
100                   std::vector<std::string>::size_type start,
101                   const std::string& fileName);
102   void BuildVirtualFolder(cmXMLWriter& xml) const;
103   void BuildVirtualFolderImpl(std::string& virtualFolders,
104                               const std::string& prefix) const;
105   void BuildUnit(cmXMLWriter& xml, const std::string& fsPath) const;
106   void BuildUnitImpl(cmXMLWriter& xml, const std::string& virtualFolderPath,
107                      const std::string& fsPath) const;
108 };
109 
InsertPath(const std::vector<std::string> & split,std::vector<std::string>::size_type start,const std::string & fileName)110 void Tree::InsertPath(const std::vector<std::string>& split,
111                       std::vector<std::string>::size_type start,
112                       const std::string& fileName)
113 {
114   if (start == split.size()) {
115     this->files.insert(fileName);
116     return;
117   }
118   for (Tree& folder : this->folders) {
119     if (folder.path == split[start]) {
120       if (start + 1 < split.size()) {
121         folder.InsertPath(split, start + 1, fileName);
122         return;
123       }
124       // last part of split
125       folder.files.insert(fileName);
126       return;
127     }
128   }
129   // Not found in folders, thus insert
130   Tree newFolder;
131   newFolder.path = split[start];
132   if (start + 1 < split.size()) {
133     newFolder.InsertPath(split, start + 1, fileName);
134     this->folders.push_back(newFolder);
135     return;
136   }
137   // last part of split
138   newFolder.files.insert(fileName);
139   this->folders.push_back(newFolder);
140 }
141 
BuildVirtualFolder(cmXMLWriter & xml) const142 void Tree::BuildVirtualFolder(cmXMLWriter& xml) const
143 {
144   xml.StartElement("Option");
145   std::string virtualFolders = "CMake Files\\;";
146   for (Tree const& folder : this->folders) {
147     folder.BuildVirtualFolderImpl(virtualFolders, "");
148   }
149   xml.Attribute("virtualFolders", virtualFolders);
150   xml.EndElement();
151 }
152 
BuildVirtualFolderImpl(std::string & virtualFolders,const std::string & prefix) const153 void Tree::BuildVirtualFolderImpl(std::string& virtualFolders,
154                                   const std::string& prefix) const
155 {
156   virtualFolders += "CMake Files\\" + prefix + this->path + "\\;";
157   for (Tree const& folder : this->folders) {
158     folder.BuildVirtualFolderImpl(virtualFolders, prefix + this->path + "\\");
159   }
160 }
161 
BuildUnit(cmXMLWriter & xml,const std::string & fsPath) const162 void Tree::BuildUnit(cmXMLWriter& xml, const std::string& fsPath) const
163 {
164   for (std::string const& f : this->files) {
165     xml.StartElement("Unit");
166     xml.Attribute("filename", fsPath + f);
167 
168     xml.StartElement("Option");
169     xml.Attribute("virtualFolder", "CMake Files\\");
170     xml.EndElement();
171 
172     xml.EndElement();
173   }
174   for (Tree const& folder : this->folders) {
175     folder.BuildUnitImpl(xml, "", fsPath);
176   }
177 }
178 
BuildUnitImpl(cmXMLWriter & xml,const std::string & virtualFolderPath,const std::string & fsPath) const179 void Tree::BuildUnitImpl(cmXMLWriter& xml,
180                          const std::string& virtualFolderPath,
181                          const std::string& fsPath) const
182 {
183   for (std::string const& f : this->files) {
184     xml.StartElement("Unit");
185     xml.Attribute("filename", cmStrCat(fsPath, this->path, "/", f));
186 
187     xml.StartElement("Option");
188     xml.Attribute(
189       "virtualFolder",
190       cmStrCat("CMake Files\\", virtualFolderPath, this->path, "\\"));
191     xml.EndElement();
192 
193     xml.EndElement();
194   }
195   for (Tree const& folder : this->folders) {
196     folder.BuildUnitImpl(xml, cmStrCat(virtualFolderPath, this->path, "\\"),
197                          cmStrCat(fsPath, this->path, "/"));
198   }
199 }
200 
CreateNewProjectFile(const std::vector<cmLocalGenerator * > & lgs,const std::string & filename)201 void cmExtraCodeBlocksGenerator::CreateNewProjectFile(
202   const std::vector<cmLocalGenerator*>& lgs, const std::string& filename)
203 {
204   const cmMakefile* mf = lgs[0]->GetMakefile();
205   cmGeneratedFileStream fout(filename);
206   if (!fout) {
207     return;
208   }
209 
210   Tree tree;
211 
212   // build tree of virtual folders
213   for (auto const& it : this->GlobalGenerator->GetProjectMap()) {
214     // Collect all files
215     std::vector<std::string> listFiles;
216     for (cmLocalGenerator* lg : it.second) {
217       cm::append(listFiles, lg->GetMakefile()->GetListFiles());
218     }
219 
220     // Convert
221     for (std::string const& listFile : listFiles) {
222       // don't put cmake's own files into the project (#12110):
223       if (cmHasPrefix(listFile, cmSystemTools::GetCMakeRoot())) {
224         continue;
225       }
226 
227       const std::string& relative = cmSystemTools::RelativePath(
228         it.second[0]->GetSourceDirectory(), listFile);
229       std::vector<std::string> split;
230       cmSystemTools::SplitPath(relative, split, false);
231       // Split filename from path
232       std::string fileName = *(split.end() - 1);
233       split.erase(split.end() - 1, split.end());
234 
235       // We don't want paths with CMakeFiles in them
236       // or do we?
237       // In speedcrunch those where purely internal
238       //
239       // Also we can disable external (outside the project) files by setting ON
240       // CMAKE_CODEBLOCKS_EXCLUDE_EXTERNAL_FILES variable.
241       const bool excludeExternal = it.second[0]->GetMakefile()->IsOn(
242         "CMAKE_CODEBLOCKS_EXCLUDE_EXTERNAL_FILES");
243       if (!split.empty() &&
244           (!excludeExternal || (relative.find("..") == std::string::npos)) &&
245           relative.find("CMakeFiles") == std::string::npos) {
246         tree.InsertPath(split, 1, fileName);
247       }
248     }
249   }
250 
251   // figure out the compiler
252   std::string compiler = this->GetCBCompilerId(mf);
253   const std::string& make = mf->GetRequiredDefinition("CMAKE_MAKE_PROGRAM");
254   const std::string& makeArgs =
255     mf->GetSafeDefinition("CMAKE_CODEBLOCKS_MAKE_ARGUMENTS");
256 
257   cmXMLWriter xml(fout);
258   xml.StartDocument();
259   xml.StartElement("CodeBlocks_project_file");
260 
261   xml.StartElement("FileVersion");
262   xml.Attribute("major", 1);
263   xml.Attribute("minor", 6);
264   xml.EndElement();
265 
266   xml.StartElement("Project");
267 
268   xml.StartElement("Option");
269   xml.Attribute("title", lgs[0]->GetProjectName());
270   xml.EndElement();
271 
272   xml.StartElement("Option");
273   xml.Attribute("makefile_is_custom", 1);
274   xml.EndElement();
275 
276   xml.StartElement("Option");
277   xml.Attribute("compiler", compiler);
278   xml.EndElement();
279 
280   // Now build a virtual tree
281   tree.BuildVirtualFolder(xml);
282 
283   xml.StartElement("Build");
284 
285   this->AppendTarget(xml, "all", nullptr, make, lgs[0], compiler, makeArgs);
286 
287   // add all executable and library targets and some of the GLOBAL
288   // and UTILITY targets
289   for (cmLocalGenerator* lg : lgs) {
290     const auto& targets = lg->GetGeneratorTargets();
291     for (const auto& target : targets) {
292       std::string targetName = target->GetName();
293       switch (target->GetType()) {
294         case cmStateEnums::GLOBAL_TARGET: {
295           // Only add the global targets from CMAKE_BINARY_DIR,
296           // not from the subdirs
297           if (lg->GetCurrentBinaryDirectory() == lg->GetBinaryDirectory()) {
298             this->AppendTarget(xml, targetName, nullptr, make, lg, compiler,
299                                makeArgs);
300           }
301         } break;
302         case cmStateEnums::UTILITY:
303           // Add all utility targets, except the Nightly/Continuous/
304           // Experimental-"sub"targets as e.g. NightlyStart
305           if ((cmHasLiteralPrefix(targetName, "Nightly") &&
306                (targetName != "Nightly")) ||
307               (cmHasLiteralPrefix(targetName, "Continuous") &&
308                (targetName != "Continuous")) ||
309               (cmHasLiteralPrefix(targetName, "Experimental") &&
310                (targetName != "Experimental"))) {
311             break;
312           }
313 
314           this->AppendTarget(xml, targetName, nullptr, make, lg, compiler,
315                              makeArgs);
316           break;
317         case cmStateEnums::EXECUTABLE:
318         case cmStateEnums::STATIC_LIBRARY:
319         case cmStateEnums::SHARED_LIBRARY:
320         case cmStateEnums::MODULE_LIBRARY:
321         case cmStateEnums::OBJECT_LIBRARY: {
322           cmGeneratorTarget* gt = target.get();
323           this->AppendTarget(xml, targetName, gt, make, lg, compiler,
324                              makeArgs);
325           std::string fastTarget = cmStrCat(targetName, "/fast");
326           this->AppendTarget(xml, fastTarget, gt, make, lg, compiler,
327                              makeArgs);
328         } break;
329         default:
330           break;
331       }
332     }
333   }
334 
335   xml.EndElement(); // Build
336 
337   // Collect all used source files in the project.
338   // Keep a list of C/C++ source files which might have an accompanying header
339   // that should be looked for.
340   using all_files_map_t = std::map<std::string, CbpUnit>;
341   all_files_map_t allFiles;
342   std::vector<std::string> cFiles;
343 
344   auto* cm = this->GlobalGenerator->GetCMakeInstance();
345 
346   for (cmLocalGenerator* lg : lgs) {
347     cmMakefile* makefile = lg->GetMakefile();
348     const auto& targets = lg->GetGeneratorTargets();
349     for (const auto& target : targets) {
350       switch (target->GetType()) {
351         case cmStateEnums::EXECUTABLE:
352         case cmStateEnums::STATIC_LIBRARY:
353         case cmStateEnums::SHARED_LIBRARY:
354         case cmStateEnums::MODULE_LIBRARY:
355         case cmStateEnums::OBJECT_LIBRARY:
356         case cmStateEnums::UTILITY: // can have sources since 2.6.3
357         {
358           std::vector<cmSourceFile*> sources;
359           target->GetSourceFiles(
360             sources, makefile->GetSafeDefinition("CMAKE_BUILD_TYPE"));
361           for (cmSourceFile* s : sources) {
362             // don't add source files from UTILITY target which have the
363             // GENERATED property set:
364             if (target->GetType() == cmStateEnums::UTILITY &&
365                 s->GetIsGenerated()) {
366               continue;
367             }
368 
369             // check whether it is a C/C++/CUDA/HIP implementation file
370             bool isCFile = false;
371             std::string lang = s->GetOrDetermineLanguage();
372             if (lang == "C" || lang == "CXX" || lang == "CUDA" ||
373                 lang == "HIP") {
374               std::string const& srcext = s->GetExtension();
375               isCFile = cm->IsACLikeSourceExtension(srcext);
376             }
377 
378             std::string const& fullPath = s->ResolveFullPath();
379 
380             // Check file position relative to project root dir.
381             const std::string relative =
382               cmSystemTools::RelativePath(lg->GetSourceDirectory(), fullPath);
383             // Do not add this file if it has ".." in relative path and
384             // if CMAKE_CODEBLOCKS_EXCLUDE_EXTERNAL_FILES variable is on.
385             const bool excludeExternal = lg->GetMakefile()->IsOn(
386               "CMAKE_CODEBLOCKS_EXCLUDE_EXTERNAL_FILES");
387             if (excludeExternal &&
388                 (relative.find("..") != std::string::npos)) {
389               continue;
390             }
391 
392             if (isCFile) {
393               cFiles.push_back(fullPath);
394             }
395 
396             CbpUnit& cbpUnit = allFiles[fullPath];
397             cbpUnit.Targets.push_back(target.get());
398           }
399         } break;
400         default:
401           break;
402       }
403     }
404   }
405 
406   std::vector<std::string> const& headerExts =
407     this->GlobalGenerator->GetCMakeInstance()->GetHeaderExtensions();
408 
409   // The following loop tries to add header files matching to implementation
410   // files to the project. It does that by iterating over all
411   // C/C++ source files,
412   // replacing the file name extension with ".h" and checks whether such a
413   // file exists. If it does, it is inserted into the map of files.
414   // A very similar version of that code exists also in the CodeLite
415   // project generator.
416   for (std::string const& fileName : cFiles) {
417     std::string headerBasename =
418       cmStrCat(cmSystemTools::GetFilenamePath(fileName), '/',
419                cmSystemTools::GetFilenameWithoutExtension(fileName));
420 
421     // check if there's a matching header around
422     for (std::string const& ext : headerExts) {
423       std::string hname = cmStrCat(headerBasename, '.', ext);
424       // if it's already in the set, don't check if it exists on disk
425       if (allFiles.find(hname) != allFiles.end()) {
426         break;
427       }
428 
429       if (cmSystemTools::FileExists(hname)) {
430         allFiles[hname].Targets = allFiles[fileName].Targets;
431         break;
432       }
433     }
434   }
435 
436   // insert all source files in the CodeBlocks project
437   for (auto const& s : allFiles) {
438     std::string const& unitFilename = s.first;
439     CbpUnit const& unit = s.second;
440 
441     xml.StartElement("Unit");
442     xml.Attribute("filename", unitFilename);
443 
444     for (cmGeneratorTarget const* tgt : unit.Targets) {
445       xml.StartElement("Option");
446       xml.Attribute("target", tgt->GetName());
447       xml.EndElement();
448     }
449 
450     xml.EndElement();
451   }
452 
453   // Add CMakeLists.txt
454   tree.BuildUnit(xml, mf->GetHomeDirectory() + "/");
455 
456   xml.EndElement(); // Project
457   xml.EndElement(); // CodeBlocks_project_file
458   xml.EndDocument();
459 }
460 
461 // Write a dummy file for OBJECT libraries, so C::B can reference some file
CreateDummyTargetFile(cmLocalGenerator * lg,cmGeneratorTarget * target) const462 std::string cmExtraCodeBlocksGenerator::CreateDummyTargetFile(
463   cmLocalGenerator* lg, cmGeneratorTarget* target) const
464 {
465   // this file doesn't seem to be used by C::B in custom makefile mode,
466   // but we generate a unique file for each OBJECT library so in case
467   // C::B uses it in some way, the targets don't interfere with each other.
468   std::string filename = cmStrCat(lg->GetCurrentBinaryDirectory(), '/',
469                                   lg->GetTargetDirectory(target), '/',
470                                   target->GetName(), ".objlib");
471   cmGeneratedFileStream fout(filename);
472   if (fout) {
473     /* clang-format off */
474     fout << "# This is a dummy file for the OBJECT library "
475          << target->GetName()
476          << " for the CMake CodeBlocks project generator.\n"
477          << "# Don't edit, this file will be overwritten.\n";
478     /* clang-format on */
479   }
480   return filename;
481 }
482 
483 // Generate the xml code for one target.
AppendTarget(cmXMLWriter & xml,const std::string & targetName,cmGeneratorTarget * target,const std::string & make,const cmLocalGenerator * lg,const std::string & compiler,const std::string & makeFlags)484 void cmExtraCodeBlocksGenerator::AppendTarget(
485   cmXMLWriter& xml, const std::string& targetName, cmGeneratorTarget* target,
486   const std::string& make, const cmLocalGenerator* lg,
487   const std::string& compiler, const std::string& makeFlags)
488 {
489   cmMakefile const* makefile = lg->GetMakefile();
490   std::string makefileName =
491     cmStrCat(lg->GetCurrentBinaryDirectory(), "/Makefile");
492 
493   xml.StartElement("Target");
494   xml.Attribute("title", targetName);
495 
496   if (target != nullptr) {
497     int cbTargetType = this->GetCBTargetType(target);
498     std::string workingDir = lg->GetCurrentBinaryDirectory();
499     if (target->GetType() == cmStateEnums::EXECUTABLE) {
500       // Determine the directory where the executable target is created, and
501       // set the working directory to this dir.
502       cmValue runtimeOutputDir =
503         makefile->GetDefinition("CMAKE_RUNTIME_OUTPUT_DIRECTORY");
504       if (runtimeOutputDir) {
505         workingDir = *runtimeOutputDir;
506       } else {
507         cmValue executableOutputDir =
508           makefile->GetDefinition("EXECUTABLE_OUTPUT_PATH");
509         if (executableOutputDir) {
510           workingDir = *executableOutputDir;
511         }
512       }
513     }
514 
515     std::string buildType = makefile->GetSafeDefinition("CMAKE_BUILD_TYPE");
516     std::string location;
517     if (target->GetType() == cmStateEnums::OBJECT_LIBRARY) {
518       location =
519         this->CreateDummyTargetFile(const_cast<cmLocalGenerator*>(lg), target);
520     } else {
521       location = target->GetLocation(buildType);
522     }
523 
524     xml.StartElement("Option");
525     xml.Attribute("output", location);
526     xml.Attribute("prefix_auto", 0);
527     xml.Attribute("extension_auto", 0);
528     xml.EndElement();
529 
530     xml.StartElement("Option");
531     xml.Attribute("working_dir", workingDir);
532     xml.EndElement();
533 
534     xml.StartElement("Option");
535     xml.Attribute("object_output", "./");
536     xml.EndElement();
537 
538     xml.StartElement("Option");
539     xml.Attribute("type", cbTargetType);
540     xml.EndElement();
541 
542     xml.StartElement("Option");
543     xml.Attribute("compiler", compiler);
544     xml.EndElement();
545 
546     xml.StartElement("Compiler");
547 
548     // the compilerdefines for this target
549     std::vector<std::string> cdefs;
550     target->GetCompileDefinitions(cdefs, buildType, "C");
551 
552     // Expand the list.
553     for (std::string const& d : cdefs) {
554       xml.StartElement("Add");
555       xml.Attribute("option", "-D" + d);
556       xml.EndElement();
557     }
558 
559     // the include directories for this target
560     std::vector<std::string> allIncludeDirs;
561     {
562       std::vector<std::string> includes;
563       lg->GetIncludeDirectories(includes, target, "C", buildType);
564       cm::append(allIncludeDirs, includes);
565     }
566 
567     std::string systemIncludeDirs = makefile->GetSafeDefinition(
568       "CMAKE_EXTRA_GENERATOR_CXX_SYSTEM_INCLUDE_DIRS");
569     if (!systemIncludeDirs.empty()) {
570       cm::append(allIncludeDirs, cmExpandedList(systemIncludeDirs));
571     }
572 
573     systemIncludeDirs = makefile->GetSafeDefinition(
574       "CMAKE_EXTRA_GENERATOR_C_SYSTEM_INCLUDE_DIRS");
575     if (!systemIncludeDirs.empty()) {
576       cm::append(allIncludeDirs, cmExpandedList(systemIncludeDirs));
577     }
578 
579     auto end = cmRemoveDuplicates(allIncludeDirs);
580 
581     for (std::string const& str : cmMakeRange(allIncludeDirs.cbegin(), end)) {
582       xml.StartElement("Add");
583       xml.Attribute("directory", str);
584       xml.EndElement();
585     }
586 
587     xml.EndElement(); // Compiler
588   } else              // e.g. all and the GLOBAL and UTILITY targets
589   {
590     xml.StartElement("Option");
591     xml.Attribute("working_dir", lg->GetCurrentBinaryDirectory());
592     xml.EndElement();
593 
594     xml.StartElement("Option");
595     xml.Attribute("type", 4);
596     xml.EndElement();
597   }
598 
599   xml.StartElement("MakeCommands");
600 
601   xml.StartElement("Build");
602   xml.Attribute(
603     "command",
604     this->BuildMakeCommand(make, makefileName, targetName, makeFlags));
605   xml.EndElement();
606 
607   xml.StartElement("CompileFile");
608   xml.Attribute(
609     "command",
610     this->BuildMakeCommand(make, makefileName, "\"$file\"", makeFlags));
611   xml.EndElement();
612 
613   xml.StartElement("Clean");
614   xml.Attribute(
615     "command", this->BuildMakeCommand(make, makefileName, "clean", makeFlags));
616   xml.EndElement();
617 
618   xml.StartElement("DistClean");
619   xml.Attribute(
620     "command", this->BuildMakeCommand(make, makefileName, "clean", makeFlags));
621   xml.EndElement();
622 
623   xml.EndElement(); // MakeCommands
624   xml.EndElement(); // Target
625 }
626 
627 // Translate the cmake compiler id into the CodeBlocks compiler id
GetCBCompilerId(const cmMakefile * mf)628 std::string cmExtraCodeBlocksGenerator::GetCBCompilerId(const cmMakefile* mf)
629 {
630   // allow the user to overwrite the detected compiler
631   std::string userCompiler =
632     mf->GetSafeDefinition("CMAKE_CODEBLOCKS_COMPILER_ID");
633   if (!userCompiler.empty()) {
634     return userCompiler;
635   }
636 
637   // figure out which language to use
638   // for now care only for C, C++, and Fortran
639 
640   // projects with C/C++ and Fortran are handled as C/C++ projects
641   bool pureFortran = false;
642   std::string compilerIdVar;
643   if (this->GlobalGenerator->GetLanguageEnabled("CXX")) {
644     compilerIdVar = "CMAKE_CXX_COMPILER_ID";
645   } else if (this->GlobalGenerator->GetLanguageEnabled("C")) {
646     compilerIdVar = "CMAKE_C_COMPILER_ID";
647   } else if (this->GlobalGenerator->GetLanguageEnabled("Fortran")) {
648     compilerIdVar = "CMAKE_Fortran_COMPILER_ID";
649     pureFortran = true;
650   }
651 
652   std::string const& compilerId = mf->GetSafeDefinition(compilerIdVar);
653   std::string compiler = "gcc"; // default to gcc
654   if (compilerId == "MSVC") {
655     if (mf->IsDefinitionSet("MSVC10")) {
656       compiler = "msvc10";
657     } else {
658       compiler = "msvc8";
659     }
660   } else if (compilerId == "Borland") {
661     compiler = "bcc";
662   } else if (compilerId == "SDCC") {
663     compiler = "sdcc";
664   } else if (compilerId == "Intel") {
665     if (pureFortran && mf->IsDefinitionSet("WIN32")) {
666       compiler = "ifcwin"; // Intel Fortran for Windows (known by cbFortran)
667     } else {
668       compiler = "icc";
669     }
670   } else if (compilerId == "Watcom" || compilerId == "OpenWatcom") {
671     compiler = "ow";
672   } else if (compilerId == "Clang") {
673     compiler = "clang";
674   } else if (compilerId == "PGI") {
675     if (pureFortran) {
676       compiler = "pgifortran";
677     } else {
678       compiler = "pgi"; // does not exist as default in CodeBlocks 16.01
679     }
680   } else if (compilerId == "GNU") {
681     if (pureFortran) {
682       compiler = "gfortran";
683     } else {
684       compiler = "gcc";
685     }
686   }
687   return compiler;
688 }
689 
690 // Translate the cmake target type into the CodeBlocks target type id
GetCBTargetType(cmGeneratorTarget * target)691 int cmExtraCodeBlocksGenerator::GetCBTargetType(cmGeneratorTarget* target)
692 {
693   switch (target->GetType()) {
694     case cmStateEnums::EXECUTABLE:
695       if ((target->IsWin32Executable(
696             target->Makefile->GetSafeDefinition("CMAKE_BUILD_TYPE"))) ||
697           (target->GetPropertyAsBool("MACOSX_BUNDLE"))) {
698         return 0;
699       }
700       return 1;
701     case cmStateEnums::STATIC_LIBRARY:
702     case cmStateEnums::OBJECT_LIBRARY:
703       return 2;
704     case cmStateEnums::SHARED_LIBRARY:
705     case cmStateEnums::MODULE_LIBRARY:
706       return 3;
707     default:
708       return 4;
709   }
710 }
711 
712 // Create the command line for building the given target using the selected
713 // make
BuildMakeCommand(const std::string & make,const std::string & makefile,const std::string & target,const std::string & makeFlags)714 std::string cmExtraCodeBlocksGenerator::BuildMakeCommand(
715   const std::string& make, const std::string& makefile,
716   const std::string& target, const std::string& makeFlags)
717 {
718   std::string command = make;
719   if (!makeFlags.empty()) {
720     command += " ";
721     command += makeFlags;
722   }
723 
724   std::string generator = this->GlobalGenerator->GetName();
725   if (generator == "NMake Makefiles" || generator == "NMake Makefiles JOM") {
726     // For Windows ConvertToOutputPath already adds quotes when required.
727     // These need to be escaped, see
728     // https://gitlab.kitware.com/cmake/cmake/-/issues/13952
729     std::string makefileName = cmSystemTools::ConvertToOutputPath(makefile);
730     command += " /NOLOGO /f ";
731     command += makefileName;
732     command += " VERBOSE=1 ";
733     command += target;
734   } else if (generator == "MinGW Makefiles") {
735     // no escaping of spaces in this case, see
736     // https://gitlab.kitware.com/cmake/cmake/-/issues/10014
737     std::string const& makefileName = makefile;
738     command += " -f \"";
739     command += makefileName;
740     command += "\" ";
741     command += " VERBOSE=1 ";
742     command += target;
743   } else if (generator == "Ninja") {
744     command += " -v ";
745     command += target;
746   } else {
747     std::string makefileName = cmSystemTools::ConvertToOutputPath(makefile);
748     command += " -f \"";
749     command += makefileName;
750     command += "\" ";
751     command += " VERBOSE=1 ";
752     command += target;
753   }
754   return command;
755 }
756