1
0
mirror of https://github.com/Kitware/CMake.git synced 2025-06-15 16:39:26 +08:00
CMake/Source/cmExtraEclipseCDT4Generator.cxx
Brad King 15fa320071 cmLocalGenerator: Factor out relative path conversion helpers
Most calls to `MaybeConvertToRelativePath` use one of our common work
directories (e.g. top of the build tree) as the local path.  Add helpers
for each of the common cases to simplify and clarify call sites.
2021-05-13 12:47:25 -04:00

1210 lines
43 KiB
C++

/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmExtraEclipseCDT4Generator.h"
#include <algorithm>
#include <cassert>
#include <cstdio>
#include <map>
#include <memory>
#include <sstream>
#include <utility>
#include "cmsys/RegularExpression.hxx"
#include "cmGeneratedFileStream.h"
#include "cmGeneratorExpression.h"
#include "cmGeneratorTarget.h"
#include "cmGlobalGenerator.h"
#include "cmLocalGenerator.h"
#include "cmMakefile.h"
#include "cmMessageType.h"
#include "cmProperty.h"
#include "cmSourceFile.h"
#include "cmSourceGroup.h"
#include "cmState.h"
#include "cmStateTypes.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
#include "cmXMLWriter.h"
#include "cmake.h"
static void AppendAttribute(cmXMLWriter& xml, const char* keyval)
{
xml.StartElement("attribute");
xml.Attribute("key", keyval);
xml.Attribute("value", keyval);
xml.EndElement();
}
template <typename T>
void AppendDictionary(cmXMLWriter& xml, const char* key, T const& value)
{
xml.StartElement("dictionary");
xml.Element("key", key);
xml.Element("value", value);
xml.EndElement();
}
cmExtraEclipseCDT4Generator::cmExtraEclipseCDT4Generator()
{
this->IsOutOfSourceBuild = false;
this->GenerateSourceProject = false;
this->SupportsVirtualFolders = true;
this->GenerateLinkedResources = true;
this->SupportsGmakeErrorParser = true;
this->SupportsMachO64Parser = true;
this->CEnabled = false;
this->CXXEnabled = false;
}
cmExternalMakefileProjectGeneratorFactory*
cmExtraEclipseCDT4Generator::GetFactory()
{
static cmExternalMakefileProjectGeneratorSimpleFactory<
cmExtraEclipseCDT4Generator>
factory("Eclipse CDT4", "Generates Eclipse CDT 4.0 project files.");
if (factory.GetSupportedGlobalGenerators().empty()) {
// TODO: Verify if __CYGWIN__ should be checked.
//#if defined(_WIN32) && !defined(__CYGWIN__)
#if defined(_WIN32)
factory.AddSupportedGlobalGenerator("NMake Makefiles");
factory.AddSupportedGlobalGenerator("MinGW Makefiles");
// factory.AddSupportedGlobalGenerator("MSYS Makefiles");
#endif
factory.AddSupportedGlobalGenerator("Ninja");
factory.AddSupportedGlobalGenerator("Unix Makefiles");
}
return &factory;
}
void cmExtraEclipseCDT4Generator::EnableLanguage(
std::vector<std::string> const& languages, cmMakefile* /*unused*/,
bool /*optional*/)
{
for (std::string const& l : languages) {
if (l == "CXX") {
this->Natures.insert("org.eclipse.cdt.core.ccnature");
this->Natures.insert("org.eclipse.cdt.core.cnature");
this->CXXEnabled = true;
} else if (l == "C") {
this->Natures.insert("org.eclipse.cdt.core.cnature");
this->CEnabled = true;
} else if (l == "Java") {
this->Natures.insert("org.eclipse.jdt.core.javanature");
}
}
}
void cmExtraEclipseCDT4Generator::Generate()
{
const auto& lg = this->GlobalGenerator->GetLocalGenerators()[0];
const cmMakefile* mf = lg->GetMakefile();
std::string eclipseVersion = mf->GetSafeDefinition("CMAKE_ECLIPSE_VERSION");
cmsys::RegularExpression regex(".*([0-9]+\\.[0-9]+).*");
if (regex.find(eclipseVersion)) {
unsigned int majorVersion = 0;
unsigned int minorVersion = 0;
int res =
sscanf(regex.match(1).c_str(), "%u.%u", &majorVersion, &minorVersion);
if (res == 2) {
int version = majorVersion * 1000 + minorVersion;
if (version < 3006) // 3.6 is Helios
{
this->SupportsVirtualFolders = false;
this->SupportsMachO64Parser = false;
}
if (version < 3007) // 3.7 is Indigo
{
this->SupportsGmakeErrorParser = false;
}
}
}
// TODO: Decide if these are local or member variables
this->HomeDirectory = lg->GetSourceDirectory();
this->HomeOutputDirectory = lg->GetBinaryDirectory();
this->GenerateLinkedResources =
mf->IsOn("CMAKE_ECLIPSE_GENERATE_LINKED_RESOURCES");
this->IsOutOfSourceBuild =
(this->HomeDirectory != this->HomeOutputDirectory);
this->GenerateSourceProject =
(this->IsOutOfSourceBuild &&
mf->IsOn("CMAKE_ECLIPSE_GENERATE_SOURCE_PROJECT"));
if (!this->GenerateSourceProject &&
(mf->IsOn("ECLIPSE_CDT4_GENERATE_SOURCE_PROJECT"))) {
mf->IssueMessage(
MessageType::WARNING,
"ECLIPSE_CDT4_GENERATE_SOURCE_PROJECT is set to TRUE, "
"but this variable is not supported anymore since CMake 2.8.7.\n"
"Enable CMAKE_ECLIPSE_GENERATE_SOURCE_PROJECT instead.");
}
if (cmSystemTools::IsSubDirectory(this->HomeOutputDirectory,
this->HomeDirectory)) {
mf->IssueMessage(MessageType::WARNING,
"The build directory is a subdirectory "
"of the source directory.\n"
"This is not supported well by Eclipse. It is strongly "
"recommended to use a build directory which is a "
"sibling of the source directory.");
}
// NOTE: This is not good, since it pollutes the source tree. However,
// Eclipse doesn't allow CVS/SVN to work when the .project is not in
// the cvs/svn root directory. Hence, this is provided as an option.
if (this->GenerateSourceProject) {
// create .project file in the source tree
this->CreateSourceProjectFile();
}
// create a .project file
this->CreateProjectFile();
// create a .cproject file
this->CreateCProjectFile();
// create resource settings
this->CreateSettingsResourcePrefsFile();
}
void cmExtraEclipseCDT4Generator::CreateSettingsResourcePrefsFile()
{
const auto& lg = this->GlobalGenerator->GetLocalGenerators()[0];
cmMakefile* mf = lg->GetMakefile();
const std::string filename =
this->HomeOutputDirectory + "/.settings/org.eclipse.core.resources.prefs";
cmGeneratedFileStream fout(filename);
if (!fout) {
return;
}
fout << "eclipse.preferences.version=1\n";
cmProp encoding = mf->GetDefinition("CMAKE_ECLIPSE_RESOURCE_ENCODING");
if (encoding) {
fout << "encoding/<project>=" << *encoding << '\n';
}
}
void cmExtraEclipseCDT4Generator::CreateSourceProjectFile()
{
assert(this->HomeDirectory != this->HomeOutputDirectory);
// set up the project name: <project>-Source@<baseSourcePathName>
const auto& lg = this->GlobalGenerator->GetLocalGenerators()[0];
std::string name = cmExtraEclipseCDT4Generator::GenerateProjectName(
lg->GetProjectName(), "Source",
cmExtraEclipseCDT4Generator::GetPathBasename(this->HomeDirectory));
const std::string filename = this->HomeDirectory + "/.project";
cmGeneratedFileStream fout(filename);
if (!fout) {
return;
}
cmXMLWriter xml(fout);
xml.StartDocument("UTF-8");
xml.StartElement("projectDescription");
xml.Element("name", name);
xml.Element("comment", "");
xml.Element("projects", "");
xml.Element("buildSpec", "");
xml.Element("natures", "");
xml.StartElement("linkedResources");
if (this->SupportsVirtualFolders) {
this->CreateLinksToSubprojects(xml, this->HomeDirectory);
this->SrcLinkedResources.clear();
}
xml.EndElement(); // linkedResources
xml.EndElement(); // projectDescription
xml.EndDocument();
}
void cmExtraEclipseCDT4Generator::AddEnvVar(std::ostream& out,
const char* envVar,
cmLocalGenerator& lg)
{
cmMakefile* mf = lg.GetMakefile();
// get the variables from the environment and from the cache and then
// figure out which one to use:
std::string envVarValue;
const bool envVarSet = cmSystemTools::GetEnv(envVar, envVarValue);
std::string cacheEntryName = cmStrCat("CMAKE_ECLIPSE_ENVVAR_", envVar);
cmProp cacheValue = lg.GetState()->GetInitializedCacheValue(cacheEntryName);
// now we have both, decide which one to use
std::string valueToUse;
if (!envVarSet && !cacheValue) {
// nothing known, do nothing
valueToUse.clear();
} else if (envVarSet && !cacheValue) {
// The variable is in the env, but not in the cache. Use it and put it
// in the cache
valueToUse = envVarValue;
mf->AddCacheDefinition(cacheEntryName, valueToUse, cacheEntryName.c_str(),
cmStateEnums::STRING, true);
mf->GetCMakeInstance()->SaveCache(lg.GetBinaryDirectory());
} else if (!envVarSet && cacheValue) {
// It is already in the cache, but not in the env, so use it from the cache
valueToUse = *cacheValue;
} else {
// It is both in the cache and in the env.
// Use the version from the env. except if the value from the env is
// completely contained in the value from the cache (for the case that we
// now have a PATH without MSVC dirs in the env. but had the full PATH with
// all MSVC dirs during the cmake run which stored the var in the cache:
valueToUse = *cacheValue;
if (valueToUse.find(envVarValue) == std::string::npos) {
valueToUse = envVarValue;
mf->AddCacheDefinition(cacheEntryName, valueToUse,
cacheEntryName.c_str(), cmStateEnums::STRING,
true);
mf->GetCMakeInstance()->SaveCache(lg.GetBinaryDirectory());
}
}
if (!valueToUse.empty()) {
out << envVar << "=" << valueToUse << "|";
}
}
void cmExtraEclipseCDT4Generator::CreateProjectFile()
{
const auto& lg = this->GlobalGenerator->GetLocalGenerators()[0];
cmMakefile* mf = lg->GetMakefile();
const std::string filename = this->HomeOutputDirectory + "/.project";
cmGeneratedFileStream fout(filename);
if (!fout) {
return;
}
std::string compilerId = mf->GetSafeDefinition("CMAKE_C_COMPILER_ID");
if (compilerId.empty()) // no C compiler, try the C++ compiler:
{
compilerId = mf->GetSafeDefinition("CMAKE_CXX_COMPILER_ID");
}
cmXMLWriter xml(fout);
xml.StartDocument("UTF-8");
xml.StartElement("projectDescription");
xml.Element("name",
cmExtraEclipseCDT4Generator::GenerateProjectName(
lg->GetProjectName(),
mf->GetSafeDefinition("CMAKE_BUILD_TYPE"),
cmExtraEclipseCDT4Generator::GetPathBasename(
this->HomeOutputDirectory)));
xml.Element("comment", "");
xml.Element("projects", "");
xml.StartElement("buildSpec");
xml.StartElement("buildCommand");
xml.Element("name", "org.eclipse.cdt.make.core.makeBuilder");
xml.Element("triggers", "clean,full,incremental,");
xml.StartElement("arguments");
// use clean target
AppendDictionary(xml, "org.eclipse.cdt.make.core.cleanBuildTarget", "clean");
AppendDictionary(xml, "org.eclipse.cdt.make.core.enableCleanBuild", "true");
AppendDictionary(xml, "org.eclipse.cdt.make.core.append_environment",
"true");
AppendDictionary(xml, "org.eclipse.cdt.make.core.stopOnError", "true");
// set the make command
AppendDictionary(xml, "org.eclipse.cdt.make.core.enabledIncrementalBuild",
"true");
AppendDictionary(xml, "org.eclipse.cdt.make.core.build.command",
cmExtraEclipseCDT4Generator::GetEclipsePath(
mf->GetRequiredDefinition("CMAKE_MAKE_PROGRAM")));
AppendDictionary(xml, "org.eclipse.cdt.make.core.contents",
"org.eclipse.cdt.make.core.activeConfigSettings");
AppendDictionary(xml, "org.eclipse.cdt.make.core.build.target.inc", "all");
AppendDictionary(xml, "org.eclipse.cdt.make.core.build.arguments",
mf->GetSafeDefinition("CMAKE_ECLIPSE_MAKE_ARGUMENTS"));
AppendDictionary(
xml, "org.eclipse.cdt.make.core.buildLocation",
cmExtraEclipseCDT4Generator::GetEclipsePath(this->HomeOutputDirectory));
AppendDictionary(xml, "org.eclipse.cdt.make.core.useDefaultBuildCmd",
"false");
// set project specific environment
std::ostringstream environment;
environment << "VERBOSE=1|CMAKE_NO_VERBOSE=1|"; // verbose Makefile output
// set vsvars32.bat environment available at CMake time,
// but not necessarily when eclipse is open
if (compilerId == "MSVC") {
AddEnvVar(environment, "PATH", *lg);
AddEnvVar(environment, "INCLUDE", *lg);
AddEnvVar(environment, "LIB", *lg);
AddEnvVar(environment, "LIBPATH", *lg);
} else if (compilerId == "Intel") {
// if the env.var is set, use this one and put it in the cache
// if the env.var is not set, but the value is in the cache,
// use it from the cache:
AddEnvVar(environment, "INTEL_LICENSE_FILE", *lg);
}
AppendDictionary(xml, "org.eclipse.cdt.make.core.environment",
environment.str());
AppendDictionary(xml, "org.eclipse.cdt.make.core.enableFullBuild", "true");
AppendDictionary(xml, "org.eclipse.cdt.make.core.build.target.auto", "all");
AppendDictionary(xml, "org.eclipse.cdt.make.core.enableAutoBuild", "false");
AppendDictionary(xml, "org.eclipse.cdt.make.core.build.target.clean",
"clean");
AppendDictionary(xml, "org.eclipse.cdt.make.core.fullBuildTarget", "all");
AppendDictionary(xml, "org.eclipse.cdt.make.core.buildArguments", "");
AppendDictionary(
xml, "org.eclipse.cdt.make.core.build.location",
cmExtraEclipseCDT4Generator::GetEclipsePath(this->HomeOutputDirectory));
AppendDictionary(xml, "org.eclipse.cdt.make.core.autoBuildTarget", "all");
// set error parsers
std::ostringstream errorOutputParser;
if (compilerId == "MSVC") {
errorOutputParser << "org.eclipse.cdt.core.VCErrorParser;";
} else if (compilerId == "Intel") {
errorOutputParser << "org.eclipse.cdt.core.ICCErrorParser;";
}
if (this->SupportsGmakeErrorParser) {
errorOutputParser << "org.eclipse.cdt.core.GmakeErrorParser;";
} else {
errorOutputParser << "org.eclipse.cdt.core.MakeErrorParser;";
}
errorOutputParser << "org.eclipse.cdt.core.GCCErrorParser;"
"org.eclipse.cdt.core.GASErrorParser;"
"org.eclipse.cdt.core.GLDErrorParser;";
AppendDictionary(xml, "org.eclipse.cdt.core.errorOutputParser",
errorOutputParser.str());
xml.EndElement(); // arguments
xml.EndElement(); // buildCommand
xml.StartElement("buildCommand");
xml.Element("name", "org.eclipse.cdt.make.core.ScannerConfigBuilder");
xml.StartElement("arguments");
xml.EndElement(); // arguments
xml.EndElement(); // buildCommand
xml.EndElement(); // buildSpec
// set natures for c/c++ projects
xml.StartElement("natures");
xml.Element("nature", "org.eclipse.cdt.make.core.makeNature");
xml.Element("nature", "org.eclipse.cdt.make.core.ScannerConfigNature");
for (std::string const& n : this->Natures) {
xml.Element("nature", n);
}
if (cmProp extraNaturesProp =
mf->GetState()->GetGlobalProperty("ECLIPSE_EXTRA_NATURES")) {
std::vector<std::string> extraNatures = cmExpandedList(*extraNaturesProp);
for (std::string const& n : extraNatures) {
xml.Element("nature", n);
}
}
xml.EndElement(); // natures
xml.StartElement("linkedResources");
// create linked resources
if (this->IsOutOfSourceBuild) {
// create a linked resource to CMAKE_SOURCE_DIR
// (this is not done anymore for each project because of
// https://gitlab.kitware.com/cmake/cmake/-/issues/9978 and because I found
// it actually quite confusing in bigger projects with many directories and
// projects, Alex
std::string sourceLinkedResourceName = "[Source directory]";
std::string linkSourceDirectory =
cmExtraEclipseCDT4Generator::GetEclipsePath(
lg->GetCurrentSourceDirectory());
// .project dir can't be subdir of a linked resource dir
if (!cmSystemTools::IsSubDirectory(this->HomeOutputDirectory,
linkSourceDirectory)) {
cmExtraEclipseCDT4Generator::AppendLinkedResource(
xml, sourceLinkedResourceName,
cmExtraEclipseCDT4Generator::GetEclipsePath(linkSourceDirectory),
LinkToFolder);
this->SrcLinkedResources.push_back(std::move(sourceLinkedResourceName));
}
}
if (this->SupportsVirtualFolders) {
this->CreateLinksToSubprojects(xml, this->HomeOutputDirectory);
this->CreateLinksForTargets(xml);
}
xml.EndElement(); // linkedResources
xml.EndElement(); // projectDescription
}
void cmExtraEclipseCDT4Generator::WriteGroups(
std::vector<cmSourceGroup> const& sourceGroups, std::string& linkName,
cmXMLWriter& xml)
{
for (cmSourceGroup const& sg : sourceGroups) {
std::string linkName3 = cmStrCat(linkName, '/', sg.GetFullName());
std::replace(linkName3.begin(), linkName3.end(), '\\', '/');
cmExtraEclipseCDT4Generator::AppendLinkedResource(
xml, linkName3, "virtual:/virtual", VirtualFolder);
std::vector<cmSourceGroup> const& children = sg.GetGroupChildren();
if (!children.empty()) {
this->WriteGroups(children, linkName, xml);
}
std::vector<const cmSourceFile*> sFiles = sg.GetSourceFiles();
for (cmSourceFile const* file : sFiles) {
std::string const& fullPath = file->GetFullPath();
if (!cmSystemTools::FileIsDirectory(fullPath)) {
std::string linkName4 =
cmStrCat(linkName3, '/', cmSystemTools::GetFilenameName(fullPath));
cmExtraEclipseCDT4Generator::AppendLinkedResource(
xml, linkName4,
cmExtraEclipseCDT4Generator::GetEclipsePath(fullPath), LinkToFile);
}
}
}
}
void cmExtraEclipseCDT4Generator::CreateLinksForTargets(cmXMLWriter& xml)
{
std::string linkName = "[Targets]";
cmExtraEclipseCDT4Generator::AppendLinkedResource(
xml, linkName, "virtual:/virtual", VirtualFolder);
for (const auto& lg : this->GlobalGenerator->GetLocalGenerators()) {
cmMakefile* makefile = lg->GetMakefile();
const auto& targets = lg->GetGeneratorTargets();
for (const auto& target : targets) {
std::string linkName2 = cmStrCat(linkName, '/');
switch (target->GetType()) {
case cmStateEnums::EXECUTABLE:
case cmStateEnums::STATIC_LIBRARY:
case cmStateEnums::SHARED_LIBRARY:
case cmStateEnums::MODULE_LIBRARY:
case cmStateEnums::OBJECT_LIBRARY: {
const char* prefix =
(target->GetType() == cmStateEnums::EXECUTABLE ? "[exe] "
: "[lib] ");
linkName2 += prefix;
linkName2 += target->GetName();
cmExtraEclipseCDT4Generator::AppendLinkedResource(
xml, linkName2, "virtual:/virtual", VirtualFolder);
if (!this->GenerateLinkedResources) {
break; // skip generating the linked resources to the source files
}
std::vector<cmSourceGroup> sourceGroups =
makefile->GetSourceGroups();
// get the files from the source lists then add them to the groups
std::vector<cmSourceFile*> files;
target->GetSourceFiles(
files, makefile->GetSafeDefinition("CMAKE_BUILD_TYPE"));
for (cmSourceFile* sf : files) {
// Add the file to the list of sources.
std::string const& source = sf->ResolveFullPath();
cmSourceGroup* sourceGroup =
makefile->FindSourceGroup(source, sourceGroups);
sourceGroup->AssignSource(sf);
}
this->WriteGroups(sourceGroups, linkName2, xml);
} break;
// ignore all others:
default:
break;
}
}
}
}
void cmExtraEclipseCDT4Generator::CreateLinksToSubprojects(
cmXMLWriter& xml, const std::string& baseDir)
{
if (!this->GenerateLinkedResources) {
return;
}
// for each sub project create a linked resource to the source dir
// - only if it is an out-of-source build
cmExtraEclipseCDT4Generator::AppendLinkedResource(
xml, "[Subprojects]", "virtual:/virtual", VirtualFolder);
for (auto const& it : this->GlobalGenerator->GetProjectMap()) {
std::string linkSourceDirectory =
cmExtraEclipseCDT4Generator::GetEclipsePath(
it.second[0]->GetCurrentSourceDirectory());
// a linked resource must not point to a parent directory of .project or
// .project itself
if ((baseDir != linkSourceDirectory) &&
!cmSystemTools::IsSubDirectory(baseDir, linkSourceDirectory)) {
std::string linkName = cmStrCat("[Subprojects]/", it.first);
cmExtraEclipseCDT4Generator::AppendLinkedResource(
xml, linkName,
cmExtraEclipseCDT4Generator::GetEclipsePath(linkSourceDirectory),
LinkToFolder);
// Don't add it to the srcLinkedResources, because listing multiple
// directories confuses the Eclipse indexer (#13596).
}
}
}
void cmExtraEclipseCDT4Generator::AppendIncludeDirectories(
cmXMLWriter& xml, const std::vector<std::string>& includeDirs,
std::set<std::string>& emittedDirs)
{
for (std::string const& inc : includeDirs) {
if (!inc.empty()) {
std::string dir = cmSystemTools::CollapseFullPath(inc);
// handle framework include dirs on OSX, the remainder after the
// Frameworks/ part has to be stripped
// /System/Library/Frameworks/GLUT.framework/Headers
cmsys::RegularExpression frameworkRx("(.+/Frameworks)/.+\\.framework/");
if (frameworkRx.find(dir)) {
dir = frameworkRx.match(1);
}
if (emittedDirs.find(dir) == emittedDirs.end()) {
emittedDirs.insert(dir);
xml.StartElement("pathentry");
xml.Attribute("include",
cmExtraEclipseCDT4Generator::GetEclipsePath(dir));
xml.Attribute("kind", "inc");
xml.Attribute("path", "");
xml.Attribute("system", "true");
xml.EndElement();
}
}
}
}
void cmExtraEclipseCDT4Generator::CreateCProjectFile() const
{
std::set<std::string> emitted;
const auto& lg = this->GlobalGenerator->GetLocalGenerators()[0];
const cmMakefile* mf = lg->GetMakefile();
const std::string filename = this->HomeOutputDirectory + "/.cproject";
cmGeneratedFileStream fout(filename);
if (!fout) {
return;
}
cmXMLWriter xml(fout);
// add header
xml.StartDocument("UTF-8");
xml.ProcessingInstruction("fileVersion", "4.0.0");
xml.StartElement("cproject");
xml.StartElement("storageModule");
xml.Attribute("moduleId", "org.eclipse.cdt.core.settings");
xml.StartElement("cconfiguration");
xml.Attribute("id", "org.eclipse.cdt.core.default.config.1");
// Configuration settings...
xml.StartElement("storageModule");
xml.Attribute("buildSystemId",
"org.eclipse.cdt.core.defaultConfigDataProvider");
xml.Attribute("id", "org.eclipse.cdt.core.default.config.1");
xml.Attribute("moduleId", "org.eclipse.cdt.core.settings");
xml.Attribute("name", "Configuration");
xml.Element("externalSettings");
xml.StartElement("extensions");
// TODO: refactor this out...
std::string executableFormat =
mf->GetSafeDefinition("CMAKE_EXECUTABLE_FORMAT");
if (executableFormat == "ELF") {
xml.StartElement("extension");
xml.Attribute("id", "org.eclipse.cdt.core.ELF");
xml.Attribute("point", "org.eclipse.cdt.core.BinaryParser");
xml.EndElement(); // extension
xml.StartElement("extension");
xml.Attribute("id", "org.eclipse.cdt.core.GNU_ELF");
xml.Attribute("point", "org.eclipse.cdt.core.BinaryParser");
AppendAttribute(xml, "addr2line");
AppendAttribute(xml, "c++filt");
xml.EndElement(); // extension
} else {
std::string systemName = mf->GetSafeDefinition("CMAKE_SYSTEM_NAME");
if (systemName == "CYGWIN" || systemName == "MSYS") {
xml.StartElement("extension");
xml.Attribute("id", "org.eclipse.cdt.core.Cygwin_PE");
xml.Attribute("point", "org.eclipse.cdt.core.BinaryParser");
AppendAttribute(xml, "addr2line");
AppendAttribute(xml, "c++filt");
AppendAttribute(xml, "cygpath");
AppendAttribute(xml, "nm");
xml.EndElement(); // extension
} else if (systemName == "Windows") {
xml.StartElement("extension");
xml.Attribute("id", "org.eclipse.cdt.core.PE");
xml.Attribute("point", "org.eclipse.cdt.core.BinaryParser");
xml.EndElement(); // extension
} else if (systemName == "Darwin") {
xml.StartElement("extension");
xml.Attribute("id",
this->SupportsMachO64Parser
? "org.eclipse.cdt.core.MachO64"
: "org.eclipse.cdt.core.MachO");
xml.Attribute("point", "org.eclipse.cdt.core.BinaryParser");
AppendAttribute(xml, "c++filt");
xml.EndElement(); // extension
} else {
// *** Should never get here ***
xml.Element("error_toolchain_type");
}
}
xml.EndElement(); // extensions
xml.EndElement(); // storageModule
// ???
xml.StartElement("storageModule");
xml.Attribute("moduleId", "org.eclipse.cdt.core.language.mapping");
xml.Element("project-mappings");
xml.EndElement(); // storageModule
// ???
xml.StartElement("storageModule");
xml.Attribute("moduleId", "org.eclipse.cdt.core.externalSettings");
xml.EndElement(); // storageModule
// set the path entries (includes, libs, source dirs, etc.)
xml.StartElement("storageModule");
xml.Attribute("moduleId", "org.eclipse.cdt.core.pathentry");
// for each sub project with a linked resource to the source dir:
// - make it type 'src'
// - and exclude it from type 'out'
std::string excludeFromOut;
/* I don't know what the pathentry kind="src" are good for, e.g.
* autocompletion
* works also without them. Done wrong, the indexer complains, see #12417
* and #12213.
* According to #13596, this entry at least limits the directories the
* indexer is searching for files. So now the "src" entry contains only
* the linked resource to CMAKE_SOURCE_DIR.
* The CDT documentation is very terse on that:
* "CDT_SOURCE: Entry kind constant describing a path entry identifying a
* folder containing source code to be compiled."
* Also on the cdt-dev list didn't bring any information:
* http://web.archiveorange.com/archive/v/B4NlJDNIpYoOS1SbxFNy
* Alex */
// include subprojects directory to the src pathentry
// eclipse cdt indexer uses this entries as reference to index source files
if (this->GenerateLinkedResources) {
xml.StartElement("pathentry");
xml.Attribute("kind", "src");
xml.Attribute("path", "[Subprojects]");
xml.EndElement();
}
for (std::string const& p : this->SrcLinkedResources) {
xml.StartElement("pathentry");
xml.Attribute("kind", "src");
xml.Attribute("path", p);
xml.EndElement();
// exclude source directory from output search path
// - only if not named the same as an output directory
if (!cmSystemTools::FileIsDirectory(
std::string(this->HomeOutputDirectory + "/" + p))) {
excludeFromOut += p + "/|";
}
}
excludeFromOut += "**/CMakeFiles/";
xml.StartElement("pathentry");
xml.Attribute("excluding", excludeFromOut);
xml.Attribute("kind", "out");
xml.Attribute("path", "");
xml.EndElement();
// add pre-processor definitions to allow eclipse to gray out sections
emitted.clear();
for (const auto& lgen : this->GlobalGenerator->GetLocalGenerators()) {
if (cmProp cdefs =
lgen->GetMakefile()->GetProperty("COMPILE_DEFINITIONS")) {
// Expand the list.
std::vector<std::string> defs;
cmGeneratorExpression::Split(*cdefs, defs);
for (std::string const& d : defs) {
if (cmGeneratorExpression::Find(d) != std::string::npos) {
continue;
}
std::string::size_type equals = d.find('=', 0);
std::string::size_type enddef = d.length();
std::string def;
std::string val;
if (equals != std::string::npos && equals < enddef) {
// we have -DFOO=BAR
def = d.substr(0, equals);
val = d.substr(equals + 1, enddef - equals + 1);
} else {
// we have -DFOO
def = d;
}
// insert the definition if not already added.
if (emitted.find(def) == emitted.end()) {
emitted.insert(def);
xml.StartElement("pathentry");
xml.Attribute("kind", "mac");
xml.Attribute("name", def);
xml.Attribute("path", "");
xml.Attribute("value", val);
xml.EndElement();
}
}
}
}
// add system defined c macros
cmProp cDefs =
mf->GetDefinition("CMAKE_EXTRA_GENERATOR_C_SYSTEM_DEFINED_MACROS");
if (this->CEnabled && cDefs) {
// Expand the list.
std::vector<std::string> defs = cmExpandedList(*cDefs, true);
// the list must contain only definition-value pairs:
if ((defs.size() % 2) == 0) {
auto di = defs.begin();
while (di != defs.end()) {
std::string def = *di;
++di;
std::string val;
if (di != defs.end()) {
val = *di;
++di;
}
// insert the definition if not already added.
if (emitted.find(def) == emitted.end()) {
emitted.insert(def);
xml.StartElement("pathentry");
xml.Attribute("kind", "mac");
xml.Attribute("name", def);
xml.Attribute("path", "");
xml.Attribute("value", val);
xml.EndElement();
}
}
}
}
// add system defined c++ macros
cmProp cxxDefs =
mf->GetDefinition("CMAKE_EXTRA_GENERATOR_CXX_SYSTEM_DEFINED_MACROS");
if (this->CXXEnabled && cxxDefs) {
// Expand the list.
std::vector<std::string> defs = cmExpandedList(*cxxDefs, true);
// the list must contain only definition-value pairs:
if ((defs.size() % 2) == 0) {
auto di = defs.begin();
while (di != defs.end()) {
std::string def = *di;
++di;
std::string val;
if (di != defs.end()) {
val = *di;
++di;
}
// insert the definition if not already added.
if (emitted.find(def) == emitted.end()) {
emitted.insert(def);
xml.StartElement("pathentry");
xml.Attribute("kind", "mac");
xml.Attribute("name", def);
xml.Attribute("path", "");
xml.Attribute("value", val);
xml.EndElement();
}
}
}
}
// include dirs
emitted.clear();
for (const auto& lgen : this->GlobalGenerator->GetLocalGenerators()) {
const auto& targets = lgen->GetGeneratorTargets();
for (const auto& target : targets) {
if (target->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
continue;
}
std::vector<std::string> includeDirs;
std::string config = mf->GetSafeDefinition("CMAKE_BUILD_TYPE");
lgen->GetIncludeDirectories(includeDirs, target.get(), "C", config);
this->AppendIncludeDirectories(xml, includeDirs, emitted);
}
}
// now also the system include directories, in case we found them in
// CMakeSystemSpecificInformation.cmake. This makes Eclipse find the
// standard headers.
std::string compiler = mf->GetSafeDefinition("CMAKE_C_COMPILER");
if (this->CEnabled && !compiler.empty()) {
std::string systemIncludeDirs =
mf->GetSafeDefinition("CMAKE_EXTRA_GENERATOR_C_SYSTEM_INCLUDE_DIRS");
std::vector<std::string> dirs = cmExpandedList(systemIncludeDirs);
this->AppendIncludeDirectories(xml, dirs, emitted);
}
compiler = mf->GetSafeDefinition("CMAKE_CXX_COMPILER");
if (this->CXXEnabled && !compiler.empty()) {
std::string systemIncludeDirs =
mf->GetSafeDefinition("CMAKE_EXTRA_GENERATOR_CXX_SYSTEM_INCLUDE_DIRS");
std::vector<std::string> dirs = cmExpandedList(systemIncludeDirs);
this->AppendIncludeDirectories(xml, dirs, emitted);
}
xml.EndElement(); // storageModule
// add build targets
xml.StartElement("storageModule");
xml.Attribute("moduleId", "org.eclipse.cdt.make.core.buildtargets");
xml.StartElement("buildTargets");
emitted.clear();
const std::string& make = mf->GetRequiredDefinition("CMAKE_MAKE_PROGRAM");
const std::string& makeArgs =
mf->GetSafeDefinition("CMAKE_ECLIPSE_MAKE_ARGUMENTS");
cmGlobalGenerator* generator =
const_cast<cmGlobalGenerator*>(this->GlobalGenerator);
std::string allTarget;
std::string cleanTarget;
if (generator->GetAllTargetName()) {
allTarget = generator->GetAllTargetName();
}
if (generator->GetCleanTargetName()) {
cleanTarget = generator->GetCleanTargetName();
}
// add all executable and library targets and some of the GLOBAL
// and UTILITY targets
for (const auto& lgen : this->GlobalGenerator->GetLocalGenerators()) {
const auto& targets = lgen->GetGeneratorTargets();
std::string subdir =
lgen->MaybeRelativeToTopBinDir(lgen->GetCurrentBinaryDirectory());
if (subdir == ".") {
subdir.clear();
}
for (const auto& target : targets) {
std::string targetName = target->GetName();
switch (target->GetType()) {
case cmStateEnums::GLOBAL_TARGET: {
// Only add the global targets from CMAKE_BINARY_DIR,
// not from the subdirs
if (subdir.empty()) {
cmExtraEclipseCDT4Generator::AppendTarget(xml, targetName, make,
makeArgs, subdir, ": ");
}
} break;
case cmStateEnums::UTILITY:
// Add all utility targets, except the Nightly/Continuous/
// Experimental-"sub"targets as e.g. NightlyStart
if ((cmHasLiteralPrefix(targetName, "Nightly") &&
(targetName != "Nightly")) ||
(cmHasLiteralPrefix(targetName, "Continuous") &&
(targetName != "Continuous")) ||
(cmHasLiteralPrefix(targetName, "Experimental") &&
(targetName != "Experimental"))) {
break;
}
cmExtraEclipseCDT4Generator::AppendTarget(xml, targetName, make,
makeArgs, subdir, ": ");
break;
case cmStateEnums::EXECUTABLE:
case cmStateEnums::STATIC_LIBRARY:
case cmStateEnums::SHARED_LIBRARY:
case cmStateEnums::MODULE_LIBRARY:
case cmStateEnums::OBJECT_LIBRARY: {
const char* prefix =
(target->GetType() == cmStateEnums::EXECUTABLE ? "[exe] "
: "[lib] ");
cmExtraEclipseCDT4Generator::AppendTarget(xml, targetName, make,
makeArgs, subdir, prefix);
std::string fastTarget = cmStrCat(targetName, "/fast");
cmExtraEclipseCDT4Generator::AppendTarget(xml, fastTarget, make,
makeArgs, subdir, prefix);
// Add Build and Clean targets in the virtual folder of targets:
if (this->SupportsVirtualFolders) {
std::string virtDir = cmStrCat("[Targets]/", prefix, targetName);
std::string buildArgs =
cmStrCat("-C \"", lgen->GetBinaryDirectory(), "\" ", makeArgs);
cmExtraEclipseCDT4Generator::AppendTarget(
xml, "Build", make, buildArgs, virtDir, "", targetName.c_str());
std::string cleanArgs =
cmStrCat("-E chdir \"", lgen->GetCurrentBinaryDirectory(),
"\" \"", cmSystemTools::GetCMakeCommand(), "\" -P \"");
cleanArgs += lgen->GetTargetDirectory(target.get());
cleanArgs += "/cmake_clean.cmake\"";
cmExtraEclipseCDT4Generator::AppendTarget(
xml, "Clean", cmSystemTools::GetCMakeCommand(), cleanArgs,
virtDir, "", "");
}
} break;
case cmStateEnums::INTERFACE_LIBRARY:
default:
break;
}
}
// insert the all and clean targets in every subdir
if (!allTarget.empty()) {
cmExtraEclipseCDT4Generator::AppendTarget(xml, allTarget, make, makeArgs,
subdir, ": ");
}
if (!cleanTarget.empty()) {
cmExtraEclipseCDT4Generator::AppendTarget(xml, cleanTarget, make,
makeArgs, subdir, ": ");
}
// insert rules for compiling, preprocessing and assembling individual
// files
std::vector<std::string> objectFileTargets;
lg->GetIndividualFileTargets(objectFileTargets);
for (std::string const& f : objectFileTargets) {
const char* prefix = "[obj] ";
if (f.back() == 's') {
prefix = "[to asm] ";
} else if (f.back() == 'i') {
prefix = "[pre] ";
}
cmExtraEclipseCDT4Generator::AppendTarget(xml, f, make, makeArgs, subdir,
prefix);
}
}
xml.EndElement(); // buildTargets
xml.EndElement(); // storageModule
cmExtraEclipseCDT4Generator::AppendStorageScanners(xml, *mf);
xml.EndElement(); // cconfiguration
xml.EndElement(); // storageModule
xml.StartElement("storageModule");
xml.Attribute("moduleId", "cdtBuildSystem");
xml.Attribute("version", "4.0.0");
xml.StartElement("project");
xml.Attribute("id", std::string(lg->GetProjectName()) + ".null.1");
xml.Attribute("name", lg->GetProjectName());
xml.EndElement(); // project
xml.EndElement(); // storageModule
// Append additional cproject contents without applying any XML formatting
if (cmProp extraCProjectContents =
mf->GetState()->GetGlobalProperty("ECLIPSE_EXTRA_CPROJECT_CONTENTS")) {
fout << *extraCProjectContents;
}
xml.EndElement(); // cproject
}
std::string cmExtraEclipseCDT4Generator::GetEclipsePath(
const std::string& path)
{
#if defined(__CYGWIN__)
std::string cmd = "cygpath -m " + path;
std::string out;
if (!cmSystemTools::RunSingleCommand(cmd.c_str(), &out, &out)) {
return path;
} else {
out.erase(out.find_last_of('\n'));
return out;
}
#else
return path;
#endif
}
std::string cmExtraEclipseCDT4Generator::GetPathBasename(
const std::string& path)
{
std::string outputBasename = path;
while (!outputBasename.empty() &&
(outputBasename.back() == '/' || outputBasename.back() == '\\')) {
outputBasename.resize(outputBasename.size() - 1);
}
std::string::size_type loc = outputBasename.find_last_of("/\\");
if (loc != std::string::npos) {
outputBasename = outputBasename.substr(loc + 1);
}
return outputBasename;
}
std::string cmExtraEclipseCDT4Generator::GenerateProjectName(
const std::string& name, const std::string& type, const std::string& path)
{
return name + (type.empty() ? "" : "-") + type + "@" + path;
}
// Helper functions
void cmExtraEclipseCDT4Generator::AppendStorageScanners(
cmXMLWriter& xml, const cmMakefile& makefile)
{
// we need the "make" and the C (or C++) compiler which are used, Alex
const std::string& make =
makefile.GetRequiredDefinition("CMAKE_MAKE_PROGRAM");
std::string compiler = makefile.GetSafeDefinition("CMAKE_C_COMPILER");
std::string arg1 = makefile.GetSafeDefinition("CMAKE_C_COMPILER_ARG1");
if (compiler.empty()) {
compiler = makefile.GetSafeDefinition("CMAKE_CXX_COMPILER");
arg1 = makefile.GetSafeDefinition("CMAKE_CXX_COMPILER_ARG1");
}
if (compiler.empty()) // Hmm, what to do now ?
{
compiler = "gcc";
}
// the following right now hardcodes gcc behavior :-/
std::string compilerArgs =
"-E -P -v -dD ${plugin_state_location}/${specs_file}";
if (!arg1.empty()) {
arg1 += " ";
compilerArgs = arg1 + compilerArgs;
}
xml.StartElement("storageModule");
xml.Attribute("moduleId", "scannerConfiguration");
xml.StartElement("autodiscovery");
xml.Attribute("enabled", "true");
xml.Attribute("problemReportingEnabled", "true");
xml.Attribute("selectedProfileId",
"org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile");
xml.EndElement(); // autodiscovery
cmExtraEclipseCDT4Generator::AppendScannerProfile(
xml, "org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile", true,
"", true, "specsFile", compilerArgs, compiler, true, true);
cmExtraEclipseCDT4Generator::AppendScannerProfile(
xml, "org.eclipse.cdt.make.core.GCCStandardMakePerFileProfile", true, "",
true, "makefileGenerator", "-f ${project_name}_scd.mk", make, true, true);
xml.EndElement(); // storageModule
}
// The prefix is prepended before the actual name of the target. The purpose
// of that is to sort the targets in the view of Eclipse, so that at first
// the global/utility/all/clean targets appear ": ", then the executable
// targets "[exe] ", then the libraries "[lib]", then the rules for the
// object files "[obj]", then for preprocessing only "[pre] " and
// finally the assembly files "[to asm] ". Note the "to" in "to asm",
// without it, "asm" would be the first targets in the list, with the "to"
// they are the last targets, which makes more sense.
void cmExtraEclipseCDT4Generator::AppendTarget(
cmXMLWriter& xml, const std::string& target, const std::string& make,
const std::string& makeArgs, const std::string& path, const char* prefix,
const char* makeTarget)
{
xml.StartElement("target");
xml.Attribute("name", prefix + target);
xml.Attribute("path", path);
xml.Attribute("targetID", "org.eclipse.cdt.make.MakeTargetBuilder");
xml.Element("buildCommand",
cmExtraEclipseCDT4Generator::GetEclipsePath(make));
xml.Element("buildArguments", makeArgs);
xml.Element("buildTarget", makeTarget ? makeTarget : target.c_str());
xml.Element("stopOnError", "true");
xml.Element("useDefaultCommand", "false");
xml.EndElement();
}
void cmExtraEclipseCDT4Generator::AppendScannerProfile(
cmXMLWriter& xml, const std::string& profileID, bool openActionEnabled,
const std::string& openActionFilePath, bool pParserEnabled,
const std::string& scannerInfoProviderID,
const std::string& runActionArguments, const std::string& runActionCommand,
bool runActionUseDefault, bool sipParserEnabled)
{
xml.StartElement("profile");
xml.Attribute("id", profileID);
xml.StartElement("buildOutputProvider");
xml.StartElement("openAction");
xml.Attribute("enabled", openActionEnabled ? "true" : "false");
xml.Attribute("filePath", openActionFilePath);
xml.EndElement(); // openAction
xml.StartElement("parser");
xml.Attribute("enabled", pParserEnabled ? "true" : "false");
xml.EndElement(); // parser
xml.EndElement(); // buildOutputProvider
xml.StartElement("scannerInfoProvider");
xml.Attribute("id", scannerInfoProviderID);
xml.StartElement("runAction");
xml.Attribute("arguments", runActionArguments);
xml.Attribute("command", runActionCommand);
xml.Attribute("useDefault", runActionUseDefault ? "true" : "false");
xml.EndElement(); // runAction
xml.StartElement("parser");
xml.Attribute("enabled", sipParserEnabled ? "true" : "false");
xml.EndElement(); // parser
xml.EndElement(); // scannerInfoProvider
xml.EndElement(); // profile
}
void cmExtraEclipseCDT4Generator::AppendLinkedResource(cmXMLWriter& xml,
const std::string& name,
const std::string& path,
LinkType linkType)
{
const char* locationTag = "location";
int typeTag = 2;
if (linkType == VirtualFolder) // ... and not a linked folder
{
locationTag = "locationURI";
}
if (linkType == LinkToFile) {
typeTag = 1;
}
xml.StartElement("link");
xml.Element("name", name);
xml.Element("type", typeTag);
xml.Element(locationTag, path);
xml.EndElement();
}