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