resolve cyclic dependency with zstd
[platform/upstream/cmake.git] / Source / cmExtraEclipseCDT4Generator.cxx
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 "cmExtraEclipseCDT4Generator.h"
4
5 #include <algorithm>
6 #include <cassert>
7 #include <cstdio>
8 #include <map>
9 #include <memory>
10 #include <sstream>
11 #include <utility>
12
13 #include "cmsys/RegularExpression.hxx"
14
15 #include "cmGeneratedFileStream.h"
16 #include "cmGeneratorExpression.h"
17 #include "cmGeneratorTarget.h"
18 #include "cmGlobalGenerator.h"
19 #include "cmLocalGenerator.h"
20 #include "cmMakefile.h"
21 #include "cmMessageType.h"
22 #include "cmSourceFile.h"
23 #include "cmSourceGroup.h"
24 #include "cmState.h"
25 #include "cmStateTypes.h"
26 #include "cmStringAlgorithms.h"
27 #include "cmSystemTools.h"
28 #include "cmValue.h"
29 #include "cmXMLWriter.h"
30 #include "cmake.h"
31
32 static void AppendAttribute(cmXMLWriter& xml, const char* keyval)
33 {
34   xml.StartElement("attribute");
35   xml.Attribute("key", keyval);
36   xml.Attribute("value", keyval);
37   xml.EndElement();
38 }
39
40 template <typename T>
41 void AppendDictionary(cmXMLWriter& xml, const char* key, T const& value)
42 {
43   xml.StartElement("dictionary");
44   xml.Element("key", key);
45   xml.Element("value", value);
46   xml.EndElement();
47 }
48
49 cmExtraEclipseCDT4Generator::cmExtraEclipseCDT4Generator()
50 {
51   this->IsOutOfSourceBuild = false;
52   this->GenerateSourceProject = false;
53   this->SupportsVirtualFolders = true;
54   this->GenerateLinkedResources = true;
55   this->SupportsGmakeErrorParser = true;
56   this->SupportsMachO64Parser = true;
57   this->CEnabled = false;
58   this->CXXEnabled = false;
59 }
60
61 cmExternalMakefileProjectGeneratorFactory*
62 cmExtraEclipseCDT4Generator::GetFactory()
63 {
64   static cmExternalMakefileProjectGeneratorSimpleFactory<
65     cmExtraEclipseCDT4Generator>
66     factory("Eclipse CDT4", "Generates Eclipse CDT 4.0 project files.");
67
68   if (factory.GetSupportedGlobalGenerators().empty()) {
69 // TODO: Verify if __CYGWIN__ should be checked.
70 //#if defined(_WIN32) && !defined(__CYGWIN__)
71 #if defined(_WIN32)
72     factory.AddSupportedGlobalGenerator("NMake Makefiles");
73     factory.AddSupportedGlobalGenerator("MinGW Makefiles");
74 // factory.AddSupportedGlobalGenerator("MSYS Makefiles");
75 #endif
76     factory.AddSupportedGlobalGenerator("Ninja");
77     factory.AddSupportedGlobalGenerator("Unix Makefiles");
78   }
79
80   return &factory;
81 }
82
83 void cmExtraEclipseCDT4Generator::EnableLanguage(
84   std::vector<std::string> const& languages, cmMakefile* /*unused*/,
85   bool /*optional*/)
86 {
87   for (std::string const& l : languages) {
88     if (l == "CXX") {
89       this->Natures.insert("org.eclipse.cdt.core.ccnature");
90       this->Natures.insert("org.eclipse.cdt.core.cnature");
91       this->CXXEnabled = true;
92     } else if (l == "C") {
93       this->Natures.insert("org.eclipse.cdt.core.cnature");
94       this->CEnabled = true;
95     } else if (l == "Java") {
96       this->Natures.insert("org.eclipse.jdt.core.javanature");
97     }
98   }
99 }
100
101 void cmExtraEclipseCDT4Generator::Generate()
102 {
103   const auto& lg = this->GlobalGenerator->GetLocalGenerators()[0];
104   const cmMakefile* mf = lg->GetMakefile();
105
106   std::string eclipseVersion = mf->GetSafeDefinition("CMAKE_ECLIPSE_VERSION");
107   cmsys::RegularExpression regex(".*([0-9]+\\.[0-9]+).*");
108   if (regex.find(eclipseVersion)) {
109     unsigned int majorVersion = 0;
110     unsigned int minorVersion = 0;
111     int res =
112       sscanf(regex.match(1).c_str(), "%u.%u", &majorVersion, &minorVersion);
113     if (res == 2) {
114       int version = majorVersion * 1000 + minorVersion;
115       if (version < 3006) // 3.6 is Helios
116       {
117         this->SupportsVirtualFolders = false;
118         this->SupportsMachO64Parser = false;
119       }
120       if (version < 3007) // 3.7 is Indigo
121       {
122         this->SupportsGmakeErrorParser = false;
123       }
124     }
125   }
126
127   // TODO: Decide if these are local or member variables
128   this->HomeDirectory = lg->GetSourceDirectory();
129   this->HomeOutputDirectory = lg->GetBinaryDirectory();
130
131   this->GenerateLinkedResources =
132     mf->IsOn("CMAKE_ECLIPSE_GENERATE_LINKED_RESOURCES");
133
134   this->IsOutOfSourceBuild =
135     (this->HomeDirectory != this->HomeOutputDirectory);
136
137   this->GenerateSourceProject =
138     (this->IsOutOfSourceBuild &&
139      mf->IsOn("CMAKE_ECLIPSE_GENERATE_SOURCE_PROJECT"));
140
141   if (!this->GenerateSourceProject &&
142       (mf->IsOn("ECLIPSE_CDT4_GENERATE_SOURCE_PROJECT"))) {
143     mf->IssueMessage(
144       MessageType::WARNING,
145       "ECLIPSE_CDT4_GENERATE_SOURCE_PROJECT is set to TRUE, "
146       "but this variable is not supported anymore since CMake 2.8.7.\n"
147       "Enable CMAKE_ECLIPSE_GENERATE_SOURCE_PROJECT instead.");
148   }
149
150   if (cmSystemTools::IsSubDirectory(this->HomeOutputDirectory,
151                                     this->HomeDirectory)) {
152     mf->IssueMessage(MessageType::WARNING,
153                      "The build directory is a subdirectory "
154                      "of the source directory.\n"
155                      "This is not supported well by Eclipse. It is strongly "
156                      "recommended to use a build directory which is a "
157                      "sibling of the source directory.");
158   }
159
160   // NOTE: This is not good, since it pollutes the source tree. However,
161   //       Eclipse doesn't allow CVS/SVN to work when the .project is not in
162   //       the cvs/svn root directory. Hence, this is provided as an option.
163   if (this->GenerateSourceProject) {
164     // create .project file in the source tree
165     this->CreateSourceProjectFile();
166   }
167
168   // create a .project file
169   this->CreateProjectFile();
170
171   // create a .cproject file
172   this->CreateCProjectFile();
173
174   // create resource settings
175   this->CreateSettingsResourcePrefsFile();
176 }
177
178 void cmExtraEclipseCDT4Generator::CreateSettingsResourcePrefsFile()
179 {
180   const auto& lg = this->GlobalGenerator->GetLocalGenerators()[0];
181   cmMakefile* mf = lg->GetMakefile();
182
183   const std::string filename =
184     this->HomeOutputDirectory + "/.settings/org.eclipse.core.resources.prefs";
185
186   cmGeneratedFileStream fout(filename);
187   if (!fout) {
188     return;
189   }
190
191   fout << "eclipse.preferences.version=1\n";
192   cmValue encoding = mf->GetDefinition("CMAKE_ECLIPSE_RESOURCE_ENCODING");
193   if (encoding) {
194     fout << "encoding/<project>=" << *encoding << '\n';
195   }
196 }
197
198 void cmExtraEclipseCDT4Generator::CreateSourceProjectFile()
199 {
200   assert(this->HomeDirectory != this->HomeOutputDirectory);
201
202   // set up the project name: <project>-Source@<baseSourcePathName>
203   const auto& lg = this->GlobalGenerator->GetLocalGenerators()[0];
204   std::string name = cmExtraEclipseCDT4Generator::GenerateProjectName(
205     lg->GetProjectName(), "Source",
206     cmExtraEclipseCDT4Generator::GetPathBasename(this->HomeDirectory));
207
208   const std::string filename = this->HomeDirectory + "/.project";
209   cmGeneratedFileStream fout(filename);
210   if (!fout) {
211     return;
212   }
213
214   cmXMLWriter xml(fout);
215   xml.StartDocument("UTF-8");
216   xml.StartElement("projectDescription");
217   xml.Element("name", name);
218   xml.Element("comment", "");
219   xml.Element("projects", "");
220   xml.Element("buildSpec", "");
221   xml.Element("natures", "");
222   xml.StartElement("linkedResources");
223
224   if (this->SupportsVirtualFolders) {
225     this->CreateLinksToSubprojects(xml, this->HomeDirectory);
226     this->SrcLinkedResources.clear();
227   }
228
229   xml.EndElement(); // linkedResources
230   xml.EndElement(); // projectDescription
231   xml.EndDocument();
232 }
233
234 void cmExtraEclipseCDT4Generator::AddEnvVar(std::ostream& out,
235                                             const char* envVar,
236                                             cmLocalGenerator& lg)
237 {
238   cmMakefile* mf = lg.GetMakefile();
239
240   // get the variables from the environment and from the cache and then
241   // figure out which one to use:
242
243   std::string envVarValue;
244   const bool envVarSet = cmSystemTools::GetEnv(envVar, envVarValue);
245
246   std::string cacheEntryName = cmStrCat("CMAKE_ECLIPSE_ENVVAR_", envVar);
247   cmValue cacheValue = lg.GetState()->GetInitializedCacheValue(cacheEntryName);
248
249   // now we have both, decide which one to use
250   std::string valueToUse;
251   if (!envVarSet && !cacheValue) {
252     // nothing known, do nothing
253     valueToUse.clear();
254   } else if (envVarSet && !cacheValue) {
255     // The variable is in the env, but not in the cache. Use it and put it
256     // in the cache
257     valueToUse = envVarValue;
258     mf->AddCacheDefinition(cacheEntryName, valueToUse, cacheEntryName.c_str(),
259                            cmStateEnums::STRING, true);
260     mf->GetCMakeInstance()->SaveCache(lg.GetBinaryDirectory());
261   } else if (!envVarSet && cacheValue) {
262     // It is already in the cache, but not in the env, so use it from the cache
263     valueToUse = *cacheValue;
264   } else {
265     // It is both in the cache and in the env.
266     // Use the version from the env. except if the value from the env is
267     // completely contained in the value from the cache (for the case that we
268     // now have a PATH without MSVC dirs in the env. but had the full PATH with
269     // all MSVC dirs during the cmake run which stored the var in the cache:
270     valueToUse = *cacheValue;
271     if (valueToUse.find(envVarValue) == std::string::npos) {
272       valueToUse = envVarValue;
273       mf->AddCacheDefinition(cacheEntryName, valueToUse,
274                              cacheEntryName.c_str(), cmStateEnums::STRING,
275                              true);
276       mf->GetCMakeInstance()->SaveCache(lg.GetBinaryDirectory());
277     }
278   }
279
280   if (!valueToUse.empty()) {
281     out << envVar << "=" << valueToUse << "|";
282   }
283 }
284
285 void cmExtraEclipseCDT4Generator::CreateProjectFile()
286 {
287   const auto& lg = this->GlobalGenerator->GetLocalGenerators()[0];
288   cmMakefile* mf = lg->GetMakefile();
289
290   const std::string filename = this->HomeOutputDirectory + "/.project";
291
292   cmGeneratedFileStream fout(filename);
293   if (!fout) {
294     return;
295   }
296
297   std::string compilerId = mf->GetSafeDefinition("CMAKE_C_COMPILER_ID");
298   if (compilerId.empty()) // no C compiler, try the C++ compiler:
299   {
300     compilerId = mf->GetSafeDefinition("CMAKE_CXX_COMPILER_ID");
301   }
302
303   cmXMLWriter xml(fout);
304
305   xml.StartDocument("UTF-8");
306   xml.StartElement("projectDescription");
307
308   xml.Element("name",
309               cmExtraEclipseCDT4Generator::GenerateProjectName(
310                 lg->GetProjectName(),
311                 mf->GetSafeDefinition("CMAKE_BUILD_TYPE"),
312                 cmExtraEclipseCDT4Generator::GetPathBasename(
313                   this->HomeOutputDirectory)));
314
315   xml.Element("comment", "");
316   xml.Element("projects", "");
317
318   xml.StartElement("buildSpec");
319   xml.StartElement("buildCommand");
320   xml.Element("name", "org.eclipse.cdt.make.core.makeBuilder");
321   xml.Element("triggers", "clean,full,incremental,");
322   xml.StartElement("arguments");
323
324   // use clean target
325   AppendDictionary(xml, "org.eclipse.cdt.make.core.cleanBuildTarget", "clean");
326   AppendDictionary(xml, "org.eclipse.cdt.make.core.enableCleanBuild", "true");
327   AppendDictionary(xml, "org.eclipse.cdt.make.core.append_environment",
328                    "true");
329   AppendDictionary(xml, "org.eclipse.cdt.make.core.stopOnError", "true");
330
331   // set the make command
332   AppendDictionary(xml, "org.eclipse.cdt.make.core.enabledIncrementalBuild",
333                    "true");
334   AppendDictionary(xml, "org.eclipse.cdt.make.core.build.command",
335                    cmExtraEclipseCDT4Generator::GetEclipsePath(
336                      mf->GetRequiredDefinition("CMAKE_MAKE_PROGRAM")));
337   AppendDictionary(xml, "org.eclipse.cdt.make.core.contents",
338                    "org.eclipse.cdt.make.core.activeConfigSettings");
339   AppendDictionary(xml, "org.eclipse.cdt.make.core.build.target.inc", "all");
340   AppendDictionary(xml, "org.eclipse.cdt.make.core.build.arguments",
341                    mf->GetSafeDefinition("CMAKE_ECLIPSE_MAKE_ARGUMENTS"));
342   AppendDictionary(
343     xml, "org.eclipse.cdt.make.core.buildLocation",
344     cmExtraEclipseCDT4Generator::GetEclipsePath(this->HomeOutputDirectory));
345   AppendDictionary(xml, "org.eclipse.cdt.make.core.useDefaultBuildCmd",
346                    "false");
347
348   // set project specific environment
349   std::ostringstream environment;
350   environment << "VERBOSE=1|CMAKE_NO_VERBOSE=1|"; // verbose Makefile output
351   // set vsvars32.bat environment available at CMake time,
352   //   but not necessarily when eclipse is open
353   if (compilerId == "MSVC") {
354     AddEnvVar(environment, "PATH", *lg);
355     AddEnvVar(environment, "INCLUDE", *lg);
356     AddEnvVar(environment, "LIB", *lg);
357     AddEnvVar(environment, "LIBPATH", *lg);
358   } else if (compilerId == "Intel") {
359     // if the env.var is set, use this one and put it in the cache
360     // if the env.var is not set, but the value is in the cache,
361     // use it from the cache:
362     AddEnvVar(environment, "INTEL_LICENSE_FILE", *lg);
363   }
364   AppendDictionary(xml, "org.eclipse.cdt.make.core.environment",
365                    environment.str());
366
367   AppendDictionary(xml, "org.eclipse.cdt.make.core.enableFullBuild", "true");
368   AppendDictionary(xml, "org.eclipse.cdt.make.core.build.target.auto", "all");
369   AppendDictionary(xml, "org.eclipse.cdt.make.core.enableAutoBuild", "false");
370   AppendDictionary(xml, "org.eclipse.cdt.make.core.build.target.clean",
371                    "clean");
372   AppendDictionary(xml, "org.eclipse.cdt.make.core.fullBuildTarget", "all");
373   AppendDictionary(xml, "org.eclipse.cdt.make.core.buildArguments", "");
374   AppendDictionary(
375     xml, "org.eclipse.cdt.make.core.build.location",
376     cmExtraEclipseCDT4Generator::GetEclipsePath(this->HomeOutputDirectory));
377   AppendDictionary(xml, "org.eclipse.cdt.make.core.autoBuildTarget", "all");
378
379   // set error parsers
380   std::ostringstream errorOutputParser;
381
382   if (compilerId == "MSVC") {
383     errorOutputParser << "org.eclipse.cdt.core.VCErrorParser;";
384   } else if (compilerId == "Intel") {
385     errorOutputParser << "org.eclipse.cdt.core.ICCErrorParser;";
386   }
387
388   if (this->SupportsGmakeErrorParser) {
389     errorOutputParser << "org.eclipse.cdt.core.GmakeErrorParser;";
390   } else {
391     errorOutputParser << "org.eclipse.cdt.core.MakeErrorParser;";
392   }
393
394   errorOutputParser << "org.eclipse.cdt.core.GCCErrorParser;"
395                        "org.eclipse.cdt.core.GASErrorParser;"
396                        "org.eclipse.cdt.core.GLDErrorParser;";
397   AppendDictionary(xml, "org.eclipse.cdt.core.errorOutputParser",
398                    errorOutputParser.str());
399
400   xml.EndElement(); // arguments
401   xml.EndElement(); // buildCommand
402   xml.StartElement("buildCommand");
403   xml.Element("name", "org.eclipse.cdt.make.core.ScannerConfigBuilder");
404   xml.StartElement("arguments");
405   xml.EndElement(); // arguments
406   xml.EndElement(); // buildCommand
407   xml.EndElement(); // buildSpec
408
409   // set natures for c/c++ projects
410   xml.StartElement("natures");
411   xml.Element("nature", "org.eclipse.cdt.make.core.makeNature");
412   xml.Element("nature", "org.eclipse.cdt.make.core.ScannerConfigNature");
413
414   for (std::string const& n : this->Natures) {
415     xml.Element("nature", n);
416   }
417
418   if (cmValue extraNaturesProp =
419         mf->GetState()->GetGlobalProperty("ECLIPSE_EXTRA_NATURES")) {
420     std::vector<std::string> extraNatures = cmExpandedList(*extraNaturesProp);
421     for (std::string const& n : extraNatures) {
422       xml.Element("nature", n);
423     }
424   }
425
426   xml.EndElement(); // natures
427
428   xml.StartElement("linkedResources");
429   // create linked resources
430   if (this->IsOutOfSourceBuild) {
431     // create a linked resource to CMAKE_SOURCE_DIR
432     // (this is not done anymore for each project because of
433     // https://gitlab.kitware.com/cmake/cmake/-/issues/9978 and because I found
434     // it actually quite confusing in bigger projects with many directories and
435     // projects, Alex
436
437     std::string sourceLinkedResourceName = "[Source directory]";
438     std::string linkSourceDirectory =
439       cmExtraEclipseCDT4Generator::GetEclipsePath(
440         lg->GetCurrentSourceDirectory());
441     // .project dir can't be subdir of a linked resource dir
442     if (!cmSystemTools::IsSubDirectory(this->HomeOutputDirectory,
443                                        linkSourceDirectory)) {
444       cmExtraEclipseCDT4Generator::AppendLinkedResource(
445         xml, sourceLinkedResourceName,
446         cmExtraEclipseCDT4Generator::GetEclipsePath(linkSourceDirectory),
447         LinkToFolder);
448       this->SrcLinkedResources.push_back(std::move(sourceLinkedResourceName));
449     }
450   }
451
452   if (this->SupportsVirtualFolders) {
453     this->CreateLinksToSubprojects(xml, this->HomeOutputDirectory);
454
455     this->CreateLinksForTargets(xml);
456   }
457
458   xml.EndElement(); // linkedResources
459   xml.EndElement(); // projectDescription
460 }
461
462 void cmExtraEclipseCDT4Generator::WriteGroups(
463   std::vector<cmSourceGroup> const& sourceGroups, std::string& linkName,
464   cmXMLWriter& xml)
465 {
466   for (cmSourceGroup const& sg : sourceGroups) {
467     std::string linkName3 = cmStrCat(linkName, '/', sg.GetFullName());
468
469     std::replace(linkName3.begin(), linkName3.end(), '\\', '/');
470
471     cmExtraEclipseCDT4Generator::AppendLinkedResource(
472       xml, linkName3, "virtual:/virtual", VirtualFolder);
473     std::vector<cmSourceGroup> const& children = sg.GetGroupChildren();
474     if (!children.empty()) {
475       this->WriteGroups(children, linkName, xml);
476     }
477     std::vector<const cmSourceFile*> sFiles = sg.GetSourceFiles();
478     for (cmSourceFile const* file : sFiles) {
479       std::string const& fullPath = file->GetFullPath();
480
481       if (!cmSystemTools::FileIsDirectory(fullPath)) {
482         std::string linkName4 =
483           cmStrCat(linkName3, '/', cmSystemTools::GetFilenameName(fullPath));
484         cmExtraEclipseCDT4Generator::AppendLinkedResource(
485           xml, linkName4,
486           cmExtraEclipseCDT4Generator::GetEclipsePath(fullPath), LinkToFile);
487       }
488     }
489   }
490 }
491
492 void cmExtraEclipseCDT4Generator::CreateLinksForTargets(cmXMLWriter& xml)
493 {
494   std::string linkName = "[Targets]";
495   cmExtraEclipseCDT4Generator::AppendLinkedResource(
496     xml, linkName, "virtual:/virtual", VirtualFolder);
497
498   for (const auto& lg : this->GlobalGenerator->GetLocalGenerators()) {
499     cmMakefile* makefile = lg->GetMakefile();
500     const auto& targets = lg->GetGeneratorTargets();
501
502     for (const auto& target : targets) {
503       std::string linkName2 = cmStrCat(linkName, '/');
504       switch (target->GetType()) {
505         case cmStateEnums::EXECUTABLE:
506         case cmStateEnums::STATIC_LIBRARY:
507         case cmStateEnums::SHARED_LIBRARY:
508         case cmStateEnums::MODULE_LIBRARY:
509         case cmStateEnums::OBJECT_LIBRARY: {
510           const char* prefix =
511             (target->GetType() == cmStateEnums::EXECUTABLE ? "[exe] "
512                                                            : "[lib] ");
513           linkName2 += prefix;
514           linkName2 += target->GetName();
515           cmExtraEclipseCDT4Generator::AppendLinkedResource(
516             xml, linkName2, "virtual:/virtual", VirtualFolder);
517           if (!this->GenerateLinkedResources) {
518             break; // skip generating the linked resources to the source files
519           }
520           std::vector<cmSourceGroup> sourceGroups =
521             makefile->GetSourceGroups();
522           // get the files from the source lists then add them to the groups
523           std::vector<cmSourceFile*> files;
524           target->GetSourceFiles(
525             files, makefile->GetSafeDefinition("CMAKE_BUILD_TYPE"));
526           for (cmSourceFile* sf : files) {
527             // Add the file to the list of sources.
528             std::string const& source = sf->ResolveFullPath();
529             cmSourceGroup* sourceGroup =
530               makefile->FindSourceGroup(source, sourceGroups);
531             sourceGroup->AssignSource(sf);
532           }
533
534           this->WriteGroups(sourceGroups, linkName2, xml);
535         } break;
536         // ignore all others:
537         default:
538           break;
539       }
540     }
541   }
542 }
543
544 void cmExtraEclipseCDT4Generator::CreateLinksToSubprojects(
545   cmXMLWriter& xml, const std::string& baseDir)
546 {
547   if (!this->GenerateLinkedResources) {
548     return;
549   }
550
551   // for each sub project create a linked resource to the source dir
552   // - only if it is an out-of-source build
553   cmExtraEclipseCDT4Generator::AppendLinkedResource(
554     xml, "[Subprojects]", "virtual:/virtual", VirtualFolder);
555
556   for (auto const& it : this->GlobalGenerator->GetProjectMap()) {
557     std::string linkSourceDirectory =
558       cmExtraEclipseCDT4Generator::GetEclipsePath(
559         it.second[0]->GetCurrentSourceDirectory());
560     // a linked resource must not point to a parent directory of .project or
561     // .project itself
562     if ((baseDir != linkSourceDirectory) &&
563         !cmSystemTools::IsSubDirectory(baseDir, linkSourceDirectory)) {
564       std::string linkName = cmStrCat("[Subprojects]/", it.first);
565       cmExtraEclipseCDT4Generator::AppendLinkedResource(
566         xml, linkName,
567         cmExtraEclipseCDT4Generator::GetEclipsePath(linkSourceDirectory),
568         LinkToFolder);
569       // Don't add it to the srcLinkedResources, because listing multiple
570       // directories confuses the Eclipse indexer (#13596).
571     }
572   }
573 }
574
575 void cmExtraEclipseCDT4Generator::AppendIncludeDirectories(
576   cmXMLWriter& xml, const std::vector<std::string>& includeDirs,
577   std::set<std::string>& emittedDirs)
578 {
579   for (std::string const& inc : includeDirs) {
580     if (!inc.empty()) {
581       std::string dir = cmSystemTools::CollapseFullPath(inc);
582
583       // handle framework include dirs on OSX, the remainder after the
584       // Frameworks/ part has to be stripped
585       //   /System/Library/Frameworks/GLUT.framework/Headers
586       cmsys::RegularExpression frameworkRx("(.+/Frameworks)/.+\\.framework/");
587       if (frameworkRx.find(dir)) {
588         dir = frameworkRx.match(1);
589       }
590
591       if (emittedDirs.find(dir) == emittedDirs.end()) {
592         emittedDirs.insert(dir);
593         xml.StartElement("pathentry");
594         xml.Attribute("include",
595                       cmExtraEclipseCDT4Generator::GetEclipsePath(dir));
596         xml.Attribute("kind", "inc");
597         xml.Attribute("path", "");
598         xml.Attribute("system", "true");
599         xml.EndElement();
600       }
601     }
602   }
603 }
604
605 void cmExtraEclipseCDT4Generator::CreateCProjectFile() const
606 {
607   std::set<std::string> emitted;
608
609   const auto& lg = this->GlobalGenerator->GetLocalGenerators()[0];
610   const cmMakefile* mf = lg->GetMakefile();
611
612   const std::string filename = this->HomeOutputDirectory + "/.cproject";
613
614   cmGeneratedFileStream fout(filename);
615   if (!fout) {
616     return;
617   }
618
619   cmXMLWriter xml(fout);
620
621   // add header
622   xml.StartDocument("UTF-8");
623   xml.ProcessingInstruction("fileVersion", "4.0.0");
624   xml.StartElement("cproject");
625   xml.StartElement("storageModule");
626   xml.Attribute("moduleId", "org.eclipse.cdt.core.settings");
627
628   xml.StartElement("cconfiguration");
629   xml.Attribute("id", "org.eclipse.cdt.core.default.config.1");
630
631   // Configuration settings...
632   xml.StartElement("storageModule");
633   xml.Attribute("buildSystemId",
634                 "org.eclipse.cdt.core.defaultConfigDataProvider");
635   xml.Attribute("id", "org.eclipse.cdt.core.default.config.1");
636   xml.Attribute("moduleId", "org.eclipse.cdt.core.settings");
637   xml.Attribute("name", "Configuration");
638   xml.Element("externalSettings");
639   xml.StartElement("extensions");
640
641   // TODO: refactor this out...
642   std::string executableFormat =
643     mf->GetSafeDefinition("CMAKE_EXECUTABLE_FORMAT");
644   if (executableFormat == "ELF") {
645     xml.StartElement("extension");
646     xml.Attribute("id", "org.eclipse.cdt.core.ELF");
647     xml.Attribute("point", "org.eclipse.cdt.core.BinaryParser");
648     xml.EndElement(); // extension
649
650     xml.StartElement("extension");
651     xml.Attribute("id", "org.eclipse.cdt.core.GNU_ELF");
652     xml.Attribute("point", "org.eclipse.cdt.core.BinaryParser");
653     AppendAttribute(xml, "addr2line");
654     AppendAttribute(xml, "c++filt");
655     xml.EndElement(); // extension
656   } else {
657     std::string systemName = mf->GetSafeDefinition("CMAKE_SYSTEM_NAME");
658     if (systemName == "CYGWIN" || systemName == "MSYS") {
659       xml.StartElement("extension");
660       xml.Attribute("id", "org.eclipse.cdt.core.Cygwin_PE");
661       xml.Attribute("point", "org.eclipse.cdt.core.BinaryParser");
662       AppendAttribute(xml, "addr2line");
663       AppendAttribute(xml, "c++filt");
664       AppendAttribute(xml, "cygpath");
665       AppendAttribute(xml, "nm");
666       xml.EndElement(); // extension
667     } else if (systemName == "Windows") {
668       xml.StartElement("extension");
669       xml.Attribute("id", "org.eclipse.cdt.core.PE");
670       xml.Attribute("point", "org.eclipse.cdt.core.BinaryParser");
671       xml.EndElement(); // extension
672     } else if (systemName == "Darwin") {
673       xml.StartElement("extension");
674       xml.Attribute("id",
675                     this->SupportsMachO64Parser
676                       ? "org.eclipse.cdt.core.MachO64"
677                       : "org.eclipse.cdt.core.MachO");
678       xml.Attribute("point", "org.eclipse.cdt.core.BinaryParser");
679       AppendAttribute(xml, "c++filt");
680       xml.EndElement(); // extension
681     } else {
682       // *** Should never get here ***
683       xml.Element("error_toolchain_type");
684     }
685   }
686
687   xml.EndElement(); // extensions
688   xml.EndElement(); // storageModule
689
690   // ???
691   xml.StartElement("storageModule");
692   xml.Attribute("moduleId", "org.eclipse.cdt.core.language.mapping");
693   xml.Element("project-mappings");
694   xml.EndElement(); // storageModule
695
696   // ???
697   xml.StartElement("storageModule");
698   xml.Attribute("moduleId", "org.eclipse.cdt.core.externalSettings");
699   xml.EndElement(); // storageModule
700
701   // set the path entries (includes, libs, source dirs, etc.)
702   xml.StartElement("storageModule");
703   xml.Attribute("moduleId", "org.eclipse.cdt.core.pathentry");
704
705   // for each sub project with a linked resource to the source dir:
706   // - make it type 'src'
707   // - and exclude it from type 'out'
708   std::string excludeFromOut;
709   /* I don't know what the pathentry kind="src" are good for, e.g.
710    * autocompletion
711    * works also without them. Done wrong, the indexer complains, see #12417
712    * and #12213.
713    * According to #13596, this entry at least limits the directories the
714    * indexer is searching for files. So now the "src" entry contains only
715    * the linked resource to CMAKE_SOURCE_DIR.
716    * The CDT documentation is very terse on that:
717    * "CDT_SOURCE: Entry kind constant describing a path entry identifying a
718    * folder containing source code to be compiled."
719    * Also on the cdt-dev list didn't bring any information:
720    * http://web.archiveorange.com/archive/v/B4NlJDNIpYoOS1SbxFNy
721    * Alex */
722   // include subprojects directory to the src pathentry
723   // eclipse cdt indexer uses this entries as reference to index source files
724   if (this->GenerateLinkedResources) {
725     xml.StartElement("pathentry");
726     xml.Attribute("kind", "src");
727     xml.Attribute("path", "[Subprojects]");
728     xml.EndElement();
729   }
730
731   for (std::string const& p : this->SrcLinkedResources) {
732     xml.StartElement("pathentry");
733     xml.Attribute("kind", "src");
734     xml.Attribute("path", p);
735     xml.EndElement();
736
737     // exclude source directory from output search path
738     // - only if not named the same as an output directory
739     if (!cmSystemTools::FileIsDirectory(
740           cmStrCat(this->HomeOutputDirectory, '/', p))) {
741       excludeFromOut += p + "/|";
742     }
743   }
744
745   excludeFromOut += "**/CMakeFiles/";
746
747   xml.StartElement("pathentry");
748   xml.Attribute("excluding", excludeFromOut);
749   xml.Attribute("kind", "out");
750   xml.Attribute("path", "");
751   xml.EndElement();
752
753   // add pre-processor definitions to allow eclipse to gray out sections
754   emitted.clear();
755   for (const auto& lgen : this->GlobalGenerator->GetLocalGenerators()) {
756
757     if (cmValue cdefs =
758           lgen->GetMakefile()->GetProperty("COMPILE_DEFINITIONS")) {
759       // Expand the list.
760       std::vector<std::string> defs;
761       cmGeneratorExpression::Split(*cdefs, defs);
762
763       for (std::string const& d : defs) {
764         if (cmGeneratorExpression::Find(d) != std::string::npos) {
765           continue;
766         }
767
768         std::string::size_type equals = d.find('=', 0);
769         std::string::size_type enddef = d.length();
770
771         std::string def;
772         std::string val;
773         if (equals != std::string::npos && equals < enddef) {
774           // we have -DFOO=BAR
775           def = d.substr(0, equals);
776           val = d.substr(equals + 1, enddef - equals + 1);
777         } else {
778           // we have -DFOO
779           def = d;
780         }
781
782         // insert the definition if not already added.
783         if (emitted.find(def) == emitted.end()) {
784           emitted.insert(def);
785           xml.StartElement("pathentry");
786           xml.Attribute("kind", "mac");
787           xml.Attribute("name", def);
788           xml.Attribute("path", "");
789           xml.Attribute("value", val);
790           xml.EndElement();
791         }
792       }
793     }
794   }
795   // add system defined c macros
796   cmValue cDefs =
797     mf->GetDefinition("CMAKE_EXTRA_GENERATOR_C_SYSTEM_DEFINED_MACROS");
798   if (this->CEnabled && cDefs) {
799     // Expand the list.
800     std::vector<std::string> defs = cmExpandedList(*cDefs, true);
801
802     // the list must contain only definition-value pairs:
803     if ((defs.size() % 2) == 0) {
804       auto di = defs.begin();
805       while (di != defs.end()) {
806         std::string def = *di;
807         ++di;
808         std::string val;
809         if (di != defs.end()) {
810           val = *di;
811           ++di;
812         }
813
814         // insert the definition if not already added.
815         if (emitted.find(def) == emitted.end()) {
816           emitted.insert(def);
817           xml.StartElement("pathentry");
818           xml.Attribute("kind", "mac");
819           xml.Attribute("name", def);
820           xml.Attribute("path", "");
821           xml.Attribute("value", val);
822           xml.EndElement();
823         }
824       }
825     }
826   }
827   // add system defined c++ macros
828   cmValue cxxDefs =
829     mf->GetDefinition("CMAKE_EXTRA_GENERATOR_CXX_SYSTEM_DEFINED_MACROS");
830   if (this->CXXEnabled && cxxDefs) {
831     // Expand the list.
832     std::vector<std::string> defs = cmExpandedList(*cxxDefs, true);
833
834     // the list must contain only definition-value pairs:
835     if ((defs.size() % 2) == 0) {
836       auto di = defs.begin();
837       while (di != defs.end()) {
838         std::string def = *di;
839         ++di;
840         std::string val;
841         if (di != defs.end()) {
842           val = *di;
843           ++di;
844         }
845
846         // insert the definition if not already added.
847         if (emitted.find(def) == emitted.end()) {
848           emitted.insert(def);
849           xml.StartElement("pathentry");
850           xml.Attribute("kind", "mac");
851           xml.Attribute("name", def);
852           xml.Attribute("path", "");
853           xml.Attribute("value", val);
854           xml.EndElement();
855         }
856       }
857     }
858   }
859
860   // include dirs
861   emitted.clear();
862   for (const auto& lgen : this->GlobalGenerator->GetLocalGenerators()) {
863     const auto& targets = lgen->GetGeneratorTargets();
864     for (const auto& target : targets) {
865       if (target->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
866         continue;
867       }
868       std::vector<std::string> includeDirs;
869       std::string config = mf->GetSafeDefinition("CMAKE_BUILD_TYPE");
870       lgen->GetIncludeDirectories(includeDirs, target.get(), "C", config);
871       this->AppendIncludeDirectories(xml, includeDirs, emitted);
872     }
873   }
874   // now also the system include directories, in case we found them in
875   // CMakeSystemSpecificInformation.cmake. This makes Eclipse find the
876   // standard headers.
877   std::string compiler = mf->GetSafeDefinition("CMAKE_C_COMPILER");
878   if (this->CEnabled && !compiler.empty()) {
879     std::string systemIncludeDirs =
880       mf->GetSafeDefinition("CMAKE_EXTRA_GENERATOR_C_SYSTEM_INCLUDE_DIRS");
881     std::vector<std::string> dirs = cmExpandedList(systemIncludeDirs);
882     this->AppendIncludeDirectories(xml, dirs, emitted);
883   }
884   compiler = mf->GetSafeDefinition("CMAKE_CXX_COMPILER");
885   if (this->CXXEnabled && !compiler.empty()) {
886     std::string systemIncludeDirs =
887       mf->GetSafeDefinition("CMAKE_EXTRA_GENERATOR_CXX_SYSTEM_INCLUDE_DIRS");
888     std::vector<std::string> dirs = cmExpandedList(systemIncludeDirs);
889     this->AppendIncludeDirectories(xml, dirs, emitted);
890   }
891
892   xml.EndElement(); // storageModule
893
894   // add build targets
895   xml.StartElement("storageModule");
896   xml.Attribute("moduleId", "org.eclipse.cdt.make.core.buildtargets");
897   xml.StartElement("buildTargets");
898   emitted.clear();
899   const std::string& make = mf->GetRequiredDefinition("CMAKE_MAKE_PROGRAM");
900   const std::string& makeArgs =
901     mf->GetSafeDefinition("CMAKE_ECLIPSE_MAKE_ARGUMENTS");
902
903   cmGlobalGenerator* generator =
904     const_cast<cmGlobalGenerator*>(this->GlobalGenerator);
905
906   std::string allTarget;
907   std::string cleanTarget;
908   if (generator->GetAllTargetName()) {
909     allTarget = generator->GetAllTargetName();
910   }
911   if (generator->GetCleanTargetName()) {
912     cleanTarget = generator->GetCleanTargetName();
913   }
914
915   // add all executable and library targets and some of the GLOBAL
916   // and UTILITY targets
917   for (const auto& lgen : this->GlobalGenerator->GetLocalGenerators()) {
918     const auto& targets = lgen->GetGeneratorTargets();
919     std::string subdir =
920       lgen->MaybeRelativeToTopBinDir(lgen->GetCurrentBinaryDirectory());
921     if (subdir == ".") {
922       subdir.clear();
923     }
924
925     for (const auto& target : targets) {
926       std::string targetName = target->GetName();
927       switch (target->GetType()) {
928         case cmStateEnums::GLOBAL_TARGET: {
929           // Only add the global targets from CMAKE_BINARY_DIR,
930           // not from the subdirs
931           if (subdir.empty()) {
932             cmExtraEclipseCDT4Generator::AppendTarget(xml, targetName, make,
933                                                       makeArgs, subdir, ": ");
934           }
935         } break;
936         case cmStateEnums::UTILITY:
937           // Add all utility targets, except the Nightly/Continuous/
938           // Experimental-"sub"targets as e.g. NightlyStart
939           if ((cmHasLiteralPrefix(targetName, "Nightly") &&
940                (targetName != "Nightly")) ||
941               (cmHasLiteralPrefix(targetName, "Continuous") &&
942                (targetName != "Continuous")) ||
943               (cmHasLiteralPrefix(targetName, "Experimental") &&
944                (targetName != "Experimental"))) {
945             break;
946           }
947
948           cmExtraEclipseCDT4Generator::AppendTarget(xml, targetName, make,
949                                                     makeArgs, subdir, ": ");
950           break;
951         case cmStateEnums::EXECUTABLE:
952         case cmStateEnums::STATIC_LIBRARY:
953         case cmStateEnums::SHARED_LIBRARY:
954         case cmStateEnums::MODULE_LIBRARY:
955         case cmStateEnums::OBJECT_LIBRARY: {
956           const char* prefix =
957             (target->GetType() == cmStateEnums::EXECUTABLE ? "[exe] "
958                                                            : "[lib] ");
959           cmExtraEclipseCDT4Generator::AppendTarget(xml, targetName, make,
960                                                     makeArgs, subdir, prefix);
961           std::string fastTarget = cmStrCat(targetName, "/fast");
962           cmExtraEclipseCDT4Generator::AppendTarget(xml, fastTarget, make,
963                                                     makeArgs, subdir, prefix);
964
965           // Add Build and Clean targets in the virtual folder of targets:
966           if (this->SupportsVirtualFolders) {
967             std::string virtDir = cmStrCat("[Targets]/", prefix, targetName);
968             std::string buildArgs =
969               cmStrCat("-C \"", lgen->GetBinaryDirectory(), "\" ", makeArgs);
970             cmExtraEclipseCDT4Generator::AppendTarget(
971               xml, "Build", make, buildArgs, virtDir, "", targetName.c_str());
972
973             std::string cleanArgs =
974               cmStrCat("-E chdir \"", lgen->GetCurrentBinaryDirectory(),
975                        "\" \"", cmSystemTools::GetCMakeCommand(), "\" -P \"");
976             cleanArgs += lgen->GetTargetDirectory(target.get());
977             cleanArgs += "/cmake_clean.cmake\"";
978             cmExtraEclipseCDT4Generator::AppendTarget(
979               xml, "Clean", cmSystemTools::GetCMakeCommand(), cleanArgs,
980               virtDir, "", "");
981           }
982         } break;
983         case cmStateEnums::INTERFACE_LIBRARY:
984         default:
985           break;
986       }
987     }
988
989     // insert the all and clean targets in every subdir
990     if (!allTarget.empty()) {
991       cmExtraEclipseCDT4Generator::AppendTarget(xml, allTarget, make, makeArgs,
992                                                 subdir, ": ");
993     }
994     if (!cleanTarget.empty()) {
995       cmExtraEclipseCDT4Generator::AppendTarget(xml, cleanTarget, make,
996                                                 makeArgs, subdir, ": ");
997     }
998
999     // insert rules for compiling, preprocessing and assembling individual
1000     // files
1001     std::vector<std::string> objectFileTargets;
1002     lg->GetIndividualFileTargets(objectFileTargets);
1003     for (std::string const& f : objectFileTargets) {
1004       const char* prefix = "[obj] ";
1005       if (f.back() == 's') {
1006         prefix = "[to asm] ";
1007       } else if (f.back() == 'i') {
1008         prefix = "[pre] ";
1009       }
1010       cmExtraEclipseCDT4Generator::AppendTarget(xml, f, make, makeArgs, subdir,
1011                                                 prefix);
1012     }
1013   }
1014
1015   xml.EndElement(); // buildTargets
1016   xml.EndElement(); // storageModule
1017
1018   cmExtraEclipseCDT4Generator::AppendStorageScanners(xml, *mf);
1019
1020   xml.EndElement(); // cconfiguration
1021   xml.EndElement(); // storageModule
1022
1023   xml.StartElement("storageModule");
1024   xml.Attribute("moduleId", "cdtBuildSystem");
1025   xml.Attribute("version", "4.0.0");
1026
1027   xml.StartElement("project");
1028   xml.Attribute("id", std::string(lg->GetProjectName()) + ".null.1");
1029   xml.Attribute("name", lg->GetProjectName());
1030   xml.EndElement(); // project
1031
1032   xml.EndElement(); // storageModule
1033
1034   // Append additional cproject contents without applying any XML formatting
1035   if (cmValue extraCProjectContents =
1036         mf->GetState()->GetGlobalProperty("ECLIPSE_EXTRA_CPROJECT_CONTENTS")) {
1037     fout << *extraCProjectContents;
1038   }
1039
1040   xml.EndElement(); // cproject
1041 }
1042
1043 std::string cmExtraEclipseCDT4Generator::GetEclipsePath(
1044   const std::string& path)
1045 {
1046 #if defined(__CYGWIN__)
1047   std::string cmd = "cygpath -m " + path;
1048   std::string out;
1049   if (!cmSystemTools::RunSingleCommand(cmd.c_str(), &out, &out)) {
1050     return path;
1051   } else {
1052     out.erase(out.find_last_of('\n'));
1053     return out;
1054   }
1055 #else
1056   return path;
1057 #endif
1058 }
1059
1060 std::string cmExtraEclipseCDT4Generator::GetPathBasename(
1061   const std::string& path)
1062 {
1063   std::string outputBasename = path;
1064   while (!outputBasename.empty() &&
1065          (outputBasename.back() == '/' || outputBasename.back() == '\\')) {
1066     outputBasename.resize(outputBasename.size() - 1);
1067   }
1068   std::string::size_type loc = outputBasename.find_last_of("/\\");
1069   if (loc != std::string::npos) {
1070     outputBasename = outputBasename.substr(loc + 1);
1071   }
1072
1073   return outputBasename;
1074 }
1075
1076 std::string cmExtraEclipseCDT4Generator::GenerateProjectName(
1077   const std::string& name, const std::string& type, const std::string& path)
1078 {
1079   return name + (type.empty() ? "" : "-") + type + "@" + path;
1080 }
1081
1082 // Helper functions
1083 void cmExtraEclipseCDT4Generator::AppendStorageScanners(
1084   cmXMLWriter& xml, const cmMakefile& makefile)
1085 {
1086   // we need the "make" and the C (or C++) compiler which are used, Alex
1087   const std::string& make =
1088     makefile.GetRequiredDefinition("CMAKE_MAKE_PROGRAM");
1089   std::string compiler = makefile.GetSafeDefinition("CMAKE_C_COMPILER");
1090   std::string arg1 = makefile.GetSafeDefinition("CMAKE_C_COMPILER_ARG1");
1091   if (compiler.empty()) {
1092     compiler = makefile.GetSafeDefinition("CMAKE_CXX_COMPILER");
1093     arg1 = makefile.GetSafeDefinition("CMAKE_CXX_COMPILER_ARG1");
1094   }
1095   if (compiler.empty()) // Hmm, what to do now ?
1096   {
1097     compiler = "gcc";
1098   }
1099
1100   // the following right now hardcodes gcc behavior :-/
1101   std::string compilerArgs =
1102     "-E -P -v -dD ${plugin_state_location}/${specs_file}";
1103   if (!arg1.empty()) {
1104     arg1 += " ";
1105     compilerArgs = arg1 + compilerArgs;
1106   }
1107
1108   xml.StartElement("storageModule");
1109   xml.Attribute("moduleId", "scannerConfiguration");
1110
1111   xml.StartElement("autodiscovery");
1112   xml.Attribute("enabled", "true");
1113   xml.Attribute("problemReportingEnabled", "true");
1114   xml.Attribute("selectedProfileId",
1115                 "org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile");
1116   xml.EndElement(); // autodiscovery
1117
1118   cmExtraEclipseCDT4Generator::AppendScannerProfile(
1119     xml, "org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile", true,
1120     "", true, "specsFile", compilerArgs, compiler, true, true);
1121   cmExtraEclipseCDT4Generator::AppendScannerProfile(
1122     xml, "org.eclipse.cdt.make.core.GCCStandardMakePerFileProfile", true, "",
1123     true, "makefileGenerator", "-f ${project_name}_scd.mk", make, true, true);
1124
1125   xml.EndElement(); // storageModule
1126 }
1127
1128 // The prefix is prepended before the actual name of the target. The purpose
1129 // of that is to sort the targets in the view of Eclipse, so that at first
1130 // the global/utility/all/clean targets appear ": ", then the executable
1131 // targets "[exe] ", then the libraries "[lib]", then the rules for the
1132 // object files "[obj]", then for preprocessing only "[pre] " and
1133 // finally the assembly files "[to asm] ". Note the "to" in "to asm",
1134 // without it, "asm" would be the first targets in the list, with the "to"
1135 // they are the last targets, which makes more sense.
1136 void cmExtraEclipseCDT4Generator::AppendTarget(
1137   cmXMLWriter& xml, const std::string& target, const std::string& make,
1138   const std::string& makeArgs, const std::string& path, const char* prefix,
1139   const char* makeTarget)
1140 {
1141   xml.StartElement("target");
1142   xml.Attribute("name", prefix + target);
1143   xml.Attribute("path", path);
1144   xml.Attribute("targetID", "org.eclipse.cdt.make.MakeTargetBuilder");
1145   xml.Element("buildCommand",
1146               cmExtraEclipseCDT4Generator::GetEclipsePath(make));
1147   xml.Element("buildArguments", makeArgs);
1148   xml.Element("buildTarget", makeTarget ? makeTarget : target.c_str());
1149   xml.Element("stopOnError", "true");
1150   xml.Element("useDefaultCommand", "false");
1151   xml.EndElement();
1152 }
1153
1154 void cmExtraEclipseCDT4Generator::AppendScannerProfile(
1155   cmXMLWriter& xml, const std::string& profileID, bool openActionEnabled,
1156   const std::string& openActionFilePath, bool pParserEnabled,
1157   const std::string& scannerInfoProviderID,
1158   const std::string& runActionArguments, const std::string& runActionCommand,
1159   bool runActionUseDefault, bool sipParserEnabled)
1160 {
1161   xml.StartElement("profile");
1162   xml.Attribute("id", profileID);
1163
1164   xml.StartElement("buildOutputProvider");
1165   xml.StartElement("openAction");
1166   xml.Attribute("enabled", openActionEnabled ? "true" : "false");
1167   xml.Attribute("filePath", openActionFilePath);
1168   xml.EndElement(); // openAction
1169   xml.StartElement("parser");
1170   xml.Attribute("enabled", pParserEnabled ? "true" : "false");
1171   xml.EndElement(); // parser
1172   xml.EndElement(); // buildOutputProvider
1173
1174   xml.StartElement("scannerInfoProvider");
1175   xml.Attribute("id", scannerInfoProviderID);
1176   xml.StartElement("runAction");
1177   xml.Attribute("arguments", runActionArguments);
1178   xml.Attribute("command", runActionCommand);
1179   xml.Attribute("useDefault", runActionUseDefault ? "true" : "false");
1180   xml.EndElement(); // runAction
1181   xml.StartElement("parser");
1182   xml.Attribute("enabled", sipParserEnabled ? "true" : "false");
1183   xml.EndElement(); // parser
1184   xml.EndElement(); // scannerInfoProvider
1185
1186   xml.EndElement(); // profile
1187 }
1188
1189 void cmExtraEclipseCDT4Generator::AppendLinkedResource(cmXMLWriter& xml,
1190                                                        const std::string& name,
1191                                                        const std::string& path,
1192                                                        LinkType linkType)
1193 {
1194   const char* locationTag = "location";
1195   int typeTag = 2;
1196   if (linkType == VirtualFolder) // ... and not a linked folder
1197   {
1198     locationTag = "locationURI";
1199   }
1200   if (linkType == LinkToFile) {
1201     typeTag = 1;
1202   }
1203
1204   xml.StartElement("link");
1205   xml.Element("name", name);
1206   xml.Element("type", typeTag);
1207   xml.Element(locationTag, path);
1208   xml.EndElement();
1209 }