cmExtraCodeBlocksGenerator.cxx 24.4 KB
Newer Older
1 2
/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
   file Copyright.txt or https://cmake.org/licensing for details.  */
3
#include "cmExtraCodeBlocksGenerator.h"
4

5 6
#include <map>
#include <ostream>
7
#include <set>
8 9
#include <utility>

10
#include "cmAlgorithms.h"
11
#include "cmGeneratedFileStream.h"
12 13 14
#include "cmGeneratorTarget.h"
#include "cmGlobalGenerator.h"
#include "cmLocalGenerator.h"
15
#include "cmMakefile.h"
16
#include "cmRange.h"
17
#include "cmSourceFile.h"
18
#include "cmStateTypes.h"
19
#include "cmSystemTools.h"
20
#include "cmXMLWriter.h"
21
#include "cmake.h"
22

23
/* Some useful URLs:
24
Homepage:
25 26 27 28 29 30 31 32 33 34 35
http://www.codeblocks.org

File format docs:
http://wiki.codeblocks.org/index.php?title=File_formats_description
http://wiki.codeblocks.org/index.php?title=Workspace_file
http://wiki.codeblocks.org/index.php?title=Project_file

Discussion:
http://forums.codeblocks.org/index.php/topic,6789.0.html
*/

wahikihiki's avatar
wahikihiki committed
36
cmExtraCodeBlocksGenerator::cmExtraCodeBlocksGenerator() = default;
37

38 39
cmExternalMakefileProjectGeneratorFactory*
cmExtraCodeBlocksGenerator::GetFactory()
40
{
41 42 43 44 45
  static cmExternalMakefileProjectGeneratorSimpleFactory<
    cmExtraCodeBlocksGenerator>
    factory("CodeBlocks", "Generates CodeBlocks project files.");

  if (factory.GetSupportedGlobalGenerators().empty()) {
46
#if defined(_WIN32)
47 48
    factory.AddSupportedGlobalGenerator("MinGW Makefiles");
    factory.AddSupportedGlobalGenerator("NMake Makefiles");
49
    factory.AddSupportedGlobalGenerator("NMake Makefiles JOM");
50
// disable until somebody actually tests it:
51
// this->AddSupportedGlobalGenerator("MSYS Makefiles");
52
#endif
53 54 55 56 57
    factory.AddSupportedGlobalGenerator("Ninja");
    factory.AddSupportedGlobalGenerator("Unix Makefiles");
  }

  return &factory;
58 59 60 61
}

void cmExtraCodeBlocksGenerator::Generate()
{
62
  // for each sub project in the project create a codeblocks project
63
  for (auto const& it : this->GlobalGenerator->GetProjectMap()) {
64
    // create a project file
65
    this->CreateProjectFile(it.second);
66
  }
67 68
}

69
/* create the project file */
70
void cmExtraCodeBlocksGenerator::CreateProjectFile(
71
  const std::vector<cmLocalGenerator*>& lgs)
72
{
73 74
  std::string outputDir = lgs[0]->GetCurrentBinaryDirectory();
  std::string projectName = lgs[0]->GetProjectName();
75

76 77 78 79
  std::string filename = outputDir + "/";
  filename += projectName + ".cbp";
  std::string sessionFilename = outputDir + "/";
  sessionFilename += projectName + ".layout";
80

81
  this->CreateNewProjectFile(lgs, filename);
82 83
}

84 85 86
/* Tree is used to create a "Virtual Folder" in CodeBlocks, in which all
 CMake files this project depends on will be put. This means additionally
 to the "Sources" and "Headers" virtual folders of CodeBlocks, there will
87
 now also be a "CMake Files" virtual folder.
88 89 90 91
 Patch by Daniel Teske <daniel.teske AT nokia.com> (which use C::B project
 files in QtCreator).*/
struct Tree
{
92
  std::string path; // only one component of the path
93
  std::vector<Tree> folders;
94
  std::set<std::string> files;
95 96
  void InsertPath(const std::vector<std::string>& splitted,
                  std::vector<std::string>::size_type start,
97
                  const std::string& fileName);
98
  void BuildVirtualFolder(cmXMLWriter& xml) const;
99
  void BuildVirtualFolderImpl(std::string& virtualFolders,
100
                              const std::string& prefix) const;
101
  void BuildUnit(cmXMLWriter& xml, const std::string& fsPath) const;
102
  void BuildUnitImpl(cmXMLWriter& xml, const std::string& virtualFolderPath,
103 104 105
                     const std::string& fsPath) const;
};

106 107
void Tree::InsertPath(const std::vector<std::string>& splitted,
                      std::vector<std::string>::size_type start,
108 109
                      const std::string& fileName)
{
110
  if (start == splitted.size()) {
111
    files.insert(fileName);
112
    return;
113
  }
114 115
  for (Tree& folder : folders) {
    if (folder.path == splitted[start]) {
116
      if (start + 1 < splitted.size()) {
117
        folder.InsertPath(splitted, start + 1, fileName);
118 119
        return;
      }
120
      // last part of splitted
121
      folder.files.insert(fileName);
122
      return;
123
    }
124
  }
125 126
  // Not found in folders, thus insert
  Tree newFolder;
127
  newFolder.path = splitted[start];
128
  if (start + 1 < splitted.size()) {
129 130 131
    newFolder.InsertPath(splitted, start + 1, fileName);
    folders.push_back(newFolder);
    return;
132
  }
133
  // last part of splitted
134
  newFolder.files.insert(fileName);
135
  folders.push_back(newFolder);
136 137
}

138
void Tree::BuildVirtualFolder(cmXMLWriter& xml) const
139
{
140 141
  xml.StartElement("Option");
  std::string virtualFolders = "CMake Files\\;";
142 143
  for (Tree const& folder : folders) {
    folder.BuildVirtualFolderImpl(virtualFolders, "");
144
  }
145 146
  xml.Attribute("virtualFolders", virtualFolders);
  xml.EndElement();
147 148 149 150 151
}

void Tree::BuildVirtualFolderImpl(std::string& virtualFolders,
                                  const std::string& prefix) const
{
152
  virtualFolders += "CMake Files\\" + prefix + path + "\\;";
153 154
  for (Tree const& folder : folders) {
    folder.BuildVirtualFolderImpl(virtualFolders, prefix + path + "\\");
155
  }
156 157
}

158
void Tree::BuildUnit(cmXMLWriter& xml, const std::string& fsPath) const
159
{
160
  for (std::string const& f : files) {
161
    xml.StartElement("Unit");
162
    xml.Attribute("filename", fsPath + f);
163 164 165 166 167 168

    xml.StartElement("Option");
    xml.Attribute("virtualFolder", "CMake Files\\");
    xml.EndElement();

    xml.EndElement();
169
  }
170 171
  for (Tree const& folder : folders) {
    folder.BuildUnitImpl(xml, "", fsPath);
172
  }
173 174
}

175
void Tree::BuildUnitImpl(cmXMLWriter& xml,
176 177 178
                         const std::string& virtualFolderPath,
                         const std::string& fsPath) const
{
179
  for (std::string const& f : files) {
180
    xml.StartElement("Unit");
181
    xml.Attribute("filename", fsPath + path + "/" + f);
182 183 184

    xml.StartElement("Option");
    xml.Attribute("virtualFolder",
185
                  "CMake Files\\" + virtualFolderPath + path + "\\");
186 187 188
    xml.EndElement();

    xml.EndElement();
189
  }
190 191 192
  for (Tree const& folder : folders) {
    folder.BuildUnitImpl(xml, virtualFolderPath + path + "\\",
                         fsPath + path + "/");
193
  }
194 195
}

196 197
void cmExtraCodeBlocksGenerator::CreateNewProjectFile(
  const std::vector<cmLocalGenerator*>& lgs, const std::string& filename)
198
{
199
  const cmMakefile* mf = lgs[0]->GetMakefile();
200
  cmGeneratedFileStream fout(filename);
201
  if (!fout) {
202
    return;
203
  }
204

205 206 207
  Tree tree;

  // build tree of virtual folders
208
  for (auto const& it : this->GlobalGenerator->GetProjectMap()) {
Alexander Neundorf's avatar
Alexander Neundorf committed
209 210
    // Collect all files
    std::vector<std::string> listFiles;
211
    for (cmLocalGenerator* lg : it.second) {
212
      cmAppend(listFiles, lg->GetMakefile()->GetListFiles());
213
    }
214

Alexander Neundorf's avatar
Alexander Neundorf committed
215
    // Convert
216
    for (std::string const& listFile : listFiles) {
217
      // don't put cmake's own files into the project (#12110):
218
      if (listFile.find(cmSystemTools::GetCMakeRoot()) == 0) {
219
        continue;
220
      }
221

222
      const std::string& relative = cmSystemTools::RelativePath(
223
        it.second[0]->GetSourceDirectory(), listFile);
Alexander Neundorf's avatar
Alexander Neundorf committed
224
      std::vector<std::string> splitted;
225
      cmSystemTools::SplitPath(relative, splitted, false);
Alexander Neundorf's avatar
Alexander Neundorf committed
226
      // Split filename from path
227
      std::string fileName = *(splitted.end() - 1);
Alexander Neundorf's avatar
Alexander Neundorf committed
228 229 230 231 232
      splitted.erase(splitted.end() - 1, splitted.end());

      // We don't want paths with CMakeFiles in them
      // or do we?
      // In speedcrunch those where purely internal
233 234 235 236 237 238
      //
      // Also we can disable external (outside the project) files by setting ON
      // CMAKE_CODEBLOCKS_EXCLUDE_EXTERNAL_FILES variable.
      const bool excludeExternal =
        cmSystemTools::IsOn(it.second[0]->GetMakefile()->GetSafeDefinition(
          "CMAKE_CODEBLOCKS_EXCLUDE_EXTERNAL_FILES"));
239
      if (!splitted.empty() &&
240
          (!excludeExternal || (relative.find("..") == std::string::npos)) &&
241
          relative.find("CMakeFiles") == std::string::npos) {
Alexander Neundorf's avatar
Alexander Neundorf committed
242 243
        tree.InsertPath(splitted, 1, fileName);
      }
244
    }
245
  }
246

247 248
  // figure out the compiler
  std::string compiler = this->GetCBCompilerId(mf);
249 250
  const std::string& make = mf->GetRequiredDefinition("CMAKE_MAKE_PROGRAM");
  const std::string& makeArgs =
251
    mf->GetSafeDefinition("CMAKE_CODEBLOCKS_MAKE_ARGUMENTS");
252

253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280
  cmXMLWriter xml(fout);
  xml.StartDocument();
  xml.StartElement("CodeBlocks_project_file");

  xml.StartElement("FileVersion");
  xml.Attribute("major", 1);
  xml.Attribute("minor", 6);
  xml.EndElement();

  xml.StartElement("Project");

  xml.StartElement("Option");
  xml.Attribute("title", lgs[0]->GetProjectName());
  xml.EndElement();

  xml.StartElement("Option");
  xml.Attribute("makefile_is_custom", 1);
  xml.EndElement();

  xml.StartElement("Option");
  xml.Attribute("compiler", compiler);
  xml.EndElement();

  // Now build a virtual tree
  tree.BuildVirtualFolder(xml);

  xml.StartElement("Build");

281
  this->AppendTarget(xml, "all", nullptr, make, lgs[0], compiler, makeArgs);
282

283
  // add all executable and library targets and some of the GLOBAL
284
  // and UTILITY targets
285 286 287 288 289
  for (cmLocalGenerator* lg : lgs) {
    const std::vector<cmGeneratorTarget*>& targets = lg->GetGeneratorTargets();
    for (cmGeneratorTarget* target : targets) {
      std::string targetName = target->GetName();
      switch (target->GetType()) {
290
        case cmStateEnums::GLOBAL_TARGET: {
291
          // Only add the global targets from CMAKE_BINARY_DIR,
Alexander Neundorf's avatar
Alexander Neundorf committed
292
          // not from the subdirs
293
          if (lg->GetCurrentBinaryDirectory() == lg->GetBinaryDirectory()) {
294 295
            this->AppendTarget(xml, targetName, nullptr, make, lg, compiler,
                               makeArgs);
Alexander Neundorf's avatar
Alexander Neundorf committed
296
          }
297
        } break;
298
        case cmStateEnums::UTILITY:
Alexander Neundorf's avatar
Alexander Neundorf committed
299 300
          // Add all utility targets, except the Nightly/Continuous/
          // Experimental-"sub"targets as e.g. NightlyStart
301 302 303 304 305 306
          if (((targetName.find("Nightly") == 0) &&
               (targetName != "Nightly")) ||
              ((targetName.find("Continuous") == 0) &&
               (targetName != "Continuous")) ||
              ((targetName.find("Experimental") == 0) &&
               (targetName != "Experimental"))) {
307
            break;
308
          }
Alexander Neundorf's avatar
Alexander Neundorf committed
309

310 311
          this->AppendTarget(xml, targetName, nullptr, make, lg, compiler,
                             makeArgs);
Alexander Neundorf's avatar
Alexander Neundorf committed
312
          break;
313 314 315 316 317
        case cmStateEnums::EXECUTABLE:
        case cmStateEnums::STATIC_LIBRARY:
        case cmStateEnums::SHARED_LIBRARY:
        case cmStateEnums::MODULE_LIBRARY:
        case cmStateEnums::OBJECT_LIBRARY: {
318
          cmGeneratorTarget* gt = target;
319 320
          this->AppendTarget(xml, targetName, gt, make, lg, compiler,
                             makeArgs);
321
          std::string fastTarget = targetName;
Alexander Neundorf's avatar
Alexander Neundorf committed
322
          fastTarget += "/fast";
323 324
          this->AppendTarget(xml, fastTarget, gt, make, lg, compiler,
                             makeArgs);
325
        } break;
Alexander Neundorf's avatar
Alexander Neundorf committed
326 327
        default:
          break;
328 329
      }
    }
330
  }
331

332
  xml.EndElement(); // Build
333

334
  // Collect all used source files in the project.
luz.paz's avatar
luz.paz committed
335
  // Keep a list of C/C++ source files which might have an accompanying header
336 337 338 339 340
  // that should be looked for.
  typedef std::map<std::string, CbpUnit> all_files_map_t;
  all_files_map_t allFiles;
  std::vector<std::string> cFiles;

341
  auto cm = this->GlobalGenerator->GetCMakeInstance();
342

343 344 345 346 347
  for (cmLocalGenerator* lg : lgs) {
    cmMakefile* makefile = lg->GetMakefile();
    const std::vector<cmGeneratorTarget*>& targets = lg->GetGeneratorTargets();
    for (cmGeneratorTarget* target : targets) {
      switch (target->GetType()) {
348 349 350 351 352 353
        case cmStateEnums::EXECUTABLE:
        case cmStateEnums::STATIC_LIBRARY:
        case cmStateEnums::SHARED_LIBRARY:
        case cmStateEnums::MODULE_LIBRARY:
        case cmStateEnums::OBJECT_LIBRARY:
        case cmStateEnums::UTILITY: // can have sources since 2.6.3
354
        {
355
          std::vector<cmSourceFile*> sources;
356
          cmGeneratorTarget* gt = target;
357
          gt->GetSourceFiles(sources,
358
                             makefile->GetSafeDefinition("CMAKE_BUILD_TYPE"));
359
          for (cmSourceFile* s : sources) {
360 361
            // don't add source files from UTILITY target which have the
            // GENERATED property set:
362
            if (gt->GetType() == cmStateEnums::UTILITY &&
363
                s->GetIsGenerated()) {
364
              continue;
365
            }
366

367
            // check whether it is a C/C++/CUDA implementation file
368
            bool isCFile = false;
369
            std::string lang = s->GetLanguage();
370
            if (lang == "C" || lang == "CXX" || lang == "CUDA") {
371
              std::string const& srcext = s->GetExtension();
372
              isCFile = cm->IsSourceExtension(srcext);
373
            }
374

375
            std::string const& fullPath = s->GetFullPath();
376

377
            // Check file position relative to project root dir.
378
            const std::string relative =
379
              cmSystemTools::RelativePath(lg->GetSourceDirectory(), fullPath);
380 381 382
            // Do not add this file if it has ".." in relative path and
            // if CMAKE_CODEBLOCKS_EXCLUDE_EXTERNAL_FILES variable is on.
            const bool excludeExternal =
383
              cmSystemTools::IsOn(lg->GetMakefile()->GetSafeDefinition(
384 385 386 387 388 389
                "CMAKE_CODEBLOCKS_EXCLUDE_EXTERNAL_FILES"));
            if (excludeExternal &&
                (relative.find("..") != std::string::npos)) {
              continue;
            }

390
            if (isCFile) {
391
              cFiles.push_back(fullPath);
392
            }
393

394
            CbpUnit& cbpUnit = allFiles[fullPath];
395
            cbpUnit.Targets.push_back(target);
396
          }
397
        }
398 399
        default: // intended fallthrough
          break;
400 401
      }
    }
402
  }
403

404
  std::vector<std::string> const& headerExts =
405
    this->GlobalGenerator->GetCMakeInstance()->GetHeaderExtensions();
406

407
  // The following loop tries to add header files matching to implementation
408 409
  // files to the project. It does that by iterating over all
  // C/C++ source files,
410
  // replacing the file name extension with ".h" and checks whether such a
411
  // file exists. If it does, it is inserted into the map of files.
412
  // A very similar version of that code exists also in the CodeLite
413
  // project generator.
414
  for (std::string const& fileName : cFiles) {
415 416 417
    std::string headerBasename = cmSystemTools::GetFilenamePath(fileName);
    headerBasename += "/";
    headerBasename += cmSystemTools::GetFilenameWithoutExtension(fileName);
418 419

    // check if there's a matching header around
420
    for (std::string const& ext : headerExts) {
421
      std::string hname = headerBasename;
422
      hname += ".";
423
      hname += 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 432 433
        break;
      }
    }
434
  }
435

436
  // insert all source files in the CodeBlocks project
437 438 439
  for (auto const& s : allFiles) {
    std::string const& unitFilename = s.first;
    CbpUnit const& unit = s.second;
440

441 442
    xml.StartElement("Unit");
    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 457 458
  xml.EndElement(); // Project
  xml.EndElement(); // CodeBlocks_project_file
  xml.EndDocument();
459 460
}

461 462
// Write a dummy file for OBJECT libraries, so C::B can reference some file
std::string cmExtraCodeBlocksGenerator::CreateDummyTargetFile(
463
  cmLocalGenerator* lg, cmGeneratorTarget* target) const
464 465 466 467
{
  // this file doesn't seem to be used by C::B in custom makefile mode,
  // but we generate a unique file for each OBJECT library so in case
  // C::B uses it in some way, the targets don't interfere with each other.
468
  std::string filename = lg->GetCurrentBinaryDirectory();
469
  filename += "/";
470
  filename += lg->GetTargetDirectory(target);
471 472 473
  filename += "/";
  filename += target->GetName();
  filename += ".objlib";
474
  cmGeneratedFileStream fout(filename);
475
  if (fout) {
476
    /* clang-format off */
477 478 479 480
    fout << "# This is a dummy file for the OBJECT library "
         << target->GetName()
         << " for the CMake CodeBlocks project generator.\n"
         << "# Don't edit, this file will be overwritten.\n";
481
    /* clang-format on */
482
  }
483 484 485
  return filename;
}

486
// Generate the xml code for one target.
487 488
void cmExtraCodeBlocksGenerator::AppendTarget(
  cmXMLWriter& xml, const std::string& targetName, cmGeneratorTarget* target,
489 490
  const std::string& make, const cmLocalGenerator* lg,
  const std::string& compiler, const std::string& makeFlags)
491
{
492
  cmMakefile const* makefile = lg->GetMakefile();
493
  std::string makefileName = lg->GetCurrentBinaryDirectory();
494 495
  makefileName += "/Makefile";

496 497 498
  xml.StartElement("Target");
  xml.Attribute("title", targetName);

Daniel Pfeifer's avatar
Daniel Pfeifer committed
499
  if (target != nullptr) {
500
    int cbTargetType = this->GetCBTargetType(target);
501
    std::string workingDir = lg->GetCurrentBinaryDirectory();
502
    if (target->GetType() == cmStateEnums::EXECUTABLE) {
Alexander Neundorf's avatar
Alexander Neundorf committed
503 504
      // Determine the directory where the executable target is created, and
      // set the working directory to this dir.
505 506
      const char* runtimeOutputDir =
        makefile->GetDefinition("CMAKE_RUNTIME_OUTPUT_DIRECTORY");
Daniel Pfeifer's avatar
Daniel Pfeifer committed
507
      if (runtimeOutputDir != nullptr) {
Alexander Neundorf's avatar
Alexander Neundorf committed
508
        workingDir = runtimeOutputDir;
509 510 511
      } else {
        const char* executableOutputDir =
          makefile->GetDefinition("EXECUTABLE_OUTPUT_PATH");
Daniel Pfeifer's avatar
Daniel Pfeifer committed
512
        if (executableOutputDir != nullptr) {
Alexander Neundorf's avatar
Alexander Neundorf committed
513 514 515
          workingDir = executableOutputDir;
        }
      }
516
    }
Alexander Neundorf's avatar
Alexander Neundorf committed
517

518
    std::string buildType = makefile->GetSafeDefinition("CMAKE_BUILD_TYPE");
519
    std::string location;
520
    if (target->GetType() == cmStateEnums::OBJECT_LIBRARY) {
521 522 523
      location =
        this->CreateDummyTargetFile(const_cast<cmLocalGenerator*>(lg), target);
    } else {
524
      location = target->GetLocation(buildType);
525
    }
526

527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549
    xml.StartElement("Option");
    xml.Attribute("output", location);
    xml.Attribute("prefix_auto", 0);
    xml.Attribute("extension_auto", 0);
    xml.EndElement();

    xml.StartElement("Option");
    xml.Attribute("working_dir", workingDir);
    xml.EndElement();

    xml.StartElement("Option");
    xml.Attribute("object_output", "./");
    xml.EndElement();

    xml.StartElement("Option");
    xml.Attribute("type", cbTargetType);
    xml.EndElement();

    xml.StartElement("Option");
    xml.Attribute("compiler", compiler);
    xml.EndElement();

    xml.StartElement("Compiler");
550 551

    // the compilerdefines for this target
552
    std::vector<std::string> cdefs;
553
    target->GetCompileDefinitions(cdefs, buildType, "C");
554

555
    // Expand the list.
556
    for (std::string const& d : cdefs) {
557
      xml.StartElement("Add");
558
      xml.Attribute("option", "-D" + d);
559
      xml.EndElement();
560
    }
561

562
    // the include directories for this target
563
    std::vector<std::string> allIncludeDirs;
564 565 566 567 568
    {
      std::vector<std::string> includes;
      lg->GetIncludeDirectories(includes, target, "C", buildType);
      cmAppend(allIncludeDirs, includes);
    }
569 570

    std::string systemIncludeDirs = makefile->GetSafeDefinition(
571
      "CMAKE_EXTRA_GENERATOR_CXX_SYSTEM_INCLUDE_DIRS");
572
    if (!systemIncludeDirs.empty()) {
573 574
      cmAppend(allIncludeDirs,
               cmSystemTools::ExpandedListArgument(systemIncludeDirs));
575
    }
576

577
    systemIncludeDirs = makefile->GetSafeDefinition(
578
      "CMAKE_EXTRA_GENERATOR_C_SYSTEM_INCLUDE_DIRS");
579
    if (!systemIncludeDirs.empty()) {
580 581
      cmAppend(allIncludeDirs,
               cmSystemTools::ExpandedListArgument(systemIncludeDirs));
582
    }
583

584 585 586
    std::vector<std::string>::const_iterator end =
      cmRemoveDuplicates(allIncludeDirs);

587
    for (std::string const& str : cmMakeRange(allIncludeDirs.cbegin(), end)) {
588
      xml.StartElement("Add");
589
      xml.Attribute("directory", str);
590
      xml.EndElement();
591
    }
592

593
    xml.EndElement(); // Compiler
594 595
  } else              // e.g. all and the GLOBAL and UTILITY targets
  {
596 597 598 599 600 601 602
    xml.StartElement("Option");
    xml.Attribute("working_dir", lg->GetCurrentBinaryDirectory());
    xml.EndElement();

    xml.StartElement("Option");
    xml.Attribute("type", 4);
    xml.EndElement();
603
  }
604 605 606 607

  xml.StartElement("MakeCommands");

  xml.StartElement("Build");
608 609
  xml.Attribute(
    "command",
610
    this->BuildMakeCommand(make, makefileName, targetName, makeFlags));
611 612 613
  xml.EndElement();

  xml.StartElement("CompileFile");
614 615 616
  xml.Attribute(
    "command",
    this->BuildMakeCommand(make, makefileName, "\"$file\"", makeFlags));
617
  xml.EndElement();
618

619
  xml.StartElement("Clean");
620
  xml.Attribute(
621
    "command", this->BuildMakeCommand(make, makefileName, "clean", makeFlags));
622 623 624
  xml.EndElement();

  xml.StartElement("DistClean");
625
  xml.Attribute(
626
    "command", this->BuildMakeCommand(make, makefileName, "clean", makeFlags));
627 628
  xml.EndElement();

629 630
  xml.EndElement(); // MakeCommands
  xml.EndElement(); // Target
631 632
}

633
// Translate the cmake compiler id into the CodeBlocks compiler id
634 635
std::string cmExtraCodeBlocksGenerator::GetCBCompilerId(const cmMakefile* mf)
{
636 637 638 639 640 641 642
  // allow the user to overwrite the detected compiler
  std::string userCompiler =
    mf->GetSafeDefinition("CMAKE_CODEBLOCKS_COMPILER_ID");
  if (!userCompiler.empty()) {
    return userCompiler;
  }

643
  // figure out which language to use
644 645 646 647 648
  // for now care only for C, C++, and Fortran

  // projects with C/C++ and Fortran are handled as C/C++ projects
  bool pureFortran = false;
  std::string compilerIdVar;
649
  if (this->GlobalGenerator->GetLanguageEnabled("CXX")) {
650
    compilerIdVar = "CMAKE_CXX_COMPILER_ID";
651
  } else if (this->GlobalGenerator->GetLanguageEnabled("C")) {
652
    compilerIdVar = "CMAKE_C_COMPILER_ID";
653
  } else if (this->GlobalGenerator->GetLanguageEnabled("Fortran")) {
654 655
    compilerIdVar = "CMAKE_Fortran_COMPILER_ID";
    pureFortran = true;
656
  }
657

658
  std::string const& compilerId = mf->GetSafeDefinition(compilerIdVar);
659 660
  std::string compiler = "gcc"; // default to gcc
  if (compilerId == "MSVC") {
661
    if (mf->IsDefinitionSet("MSVC10")) {
662
      compiler = "msvc10";
663
    } else {
664
      compiler = "msvc8";
665
    }
666
  } else if (compilerId == "Borland") {
667
    compiler = "bcc";
668
  } else if (compilerId == "SDCC") {
669
    compiler = "sdcc";
670 671
  } else if (compilerId == "Intel") {
    if (pureFortran && mf->IsDefinitionSet("WIN32")) {
672
      compiler = "ifcwin"; // Intel Fortran for Windows (known by cbFortran)
673
    } else {
674
      compiler = "icc";
675
    }
676
  } else if (compilerId == "Watcom" || compilerId == "OpenWatcom") {
677
    compiler = "ow";
678
  } else if (compilerId == "Clang") {
679
    compiler = "clang";
680 681
  } else if (compilerId == "PGI") {
    if (pureFortran) {
682
      compiler = "pgifortran";
683
    } else {
684 685
      compiler = "pgi"; // does not exist as default in CodeBlocks 16.01
    }
686 687
  } else if (compilerId == "GNU") {
    if (pureFortran) {
688
      compiler = "gfortran";
689
    } else {
690
      compiler = "gcc";
691
    }
692
  }
693 694 695
  return compiler;
}

696
// Translate the cmake target type into the CodeBlocks target type id
697
int cmExtraCodeBlocksGenerator::GetCBTargetType(cmGeneratorTarget* target)
698
{
699
  switch (target->GetType()) {
700
    case cmStateEnums::EXECUTABLE:
701 702 703 704
      if ((target->GetPropertyAsBool("WIN32_EXECUTABLE")) ||
          (target->GetPropertyAsBool("MACOSX_BUNDLE"))) {
        return 0;
      }
705
      return 1;
706 707
    case cmStateEnums::STATIC_LIBRARY:
    case cmStateEnums::OBJECT_LIBRARY:
708
      return 2;
709 710
    case cmStateEnums::SHARED_LIBRARY:
    case cmStateEnums::MODULE_LIBRARY:
711 712 713
      return 3;
    default:
      return 4;
714
  }
715 716
}

717 718
// Create the command line for building the given target using the selected
// make
719
std::string cmExtraCodeBlocksGenerator::BuildMakeCommand(
720 721
  const std::string& make, const std::string& makefile,
  const std::string& target, const std::string& makeFlags)
722 723
{
  std::string command = make;
724
  if (!makeFlags.empty()) {
725 726
    command += " ";
    command += makeFlags;
727
  }
728

729
  std::string generator = this->GlobalGenerator->GetName();
730
  if (generator == "NMake Makefiles" || generator == "NMake Makefiles JOM") {
731 732
    // For Windows ConvertToOutputPath already adds quotes when required.
    // These need to be escaped, see
733
    // https://gitlab.kitware.com/cmake/cmake/issues/13952
734
    std::string makefileName = cmSystemTools::ConvertToOutputPath(makefile);
735
    command += " /NOLOGO /f ";
736
    command += makefileName;
737
    command += " VERBOSE=1 ";
738
    command += target;
739
  } else if (generator == "MinGW Makefiles") {
740
    // no escaping of spaces in this case, see
741
    // https://gitlab.kitware.com/cmake/cmake/issues/10014
742
    std::string const& makefileName = makefile;
743
    command += " -f \"";
744
    command += makefileName;
745
    command += "\" ";
746
    command += " VERBOSE=1 ";
747
    command += target;
748
  } else if (generator == "Ninja") {
749 750
    command += " -v ";
    command += target;
751
  } else {
752
    std::string makefileName = cmSystemTools::ConvertToOutputPath(makefile);
753
    command += " -f \"";
754
    command += makefileName;
755
    command += "\" ";
756
    command += " VERBOSE=1 ";
757
    command += target;
758
  }
759 760
  return command;
}