Files
CMake/Source/cmExtraEclipseCDT4Generator.cxx
Ben Boeckel fe0163cd23 autogen: compute short directories too
Not just short directory roots. The test suites did an improper
generator check and masked them as the generator masks were put in place
after the core development but before the autogen-specific logic tests
were created.
2025-09-02 12:56:23 -04:00

1214 lines
43 KiB
C++

/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file LICENSE.rst 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 "cmList.h"
#include "cmLocalGenerator.h"
#include "cmMakefile.h"
#include "cmMessageType.h"
#include "cmSourceFile.h"
#include "cmSourceGroup.h"
#include "cmState.h"
#include "cmStateTypes.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
#include "cmValue.h"
#include "cmXMLWriter.h"
#include "cmake.h"
static void AppendAttribute(cmXMLWriter& xml, char const* keyval)
{
xml.StartElement("attribute");
xml.Attribute("key", keyval);
xml.Attribute("value", keyval);
xml.EndElement();
}
template <typename T>
void AppendDictionary(cmXMLWriter& xml, char const* 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 (deprecated).");
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()
{
auto const& lg = this->GlobalGenerator->GetLocalGenerators()[0];
cmMakefile const* 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()
{
auto const& lg = this->GlobalGenerator->GetLocalGenerators()[0];
cmMakefile* mf = lg->GetMakefile();
std::string const filename =
this->HomeOutputDirectory + "/.settings/org.eclipse.core.resources.prefs";
cmGeneratedFileStream fout(filename);
if (!fout) {
return;
}
fout << "eclipse.preferences.version=1\n";
cmValue 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>
auto const& lg = this->GlobalGenerator->GetLocalGenerators()[0];
std::string name = cmExtraEclipseCDT4Generator::GenerateProjectName(
lg->GetProjectName(), "Source",
cmExtraEclipseCDT4Generator::GetPathBasename(this->HomeDirectory));
std::string const 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,
char const* 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;
bool const envVarSet = cmSystemTools::GetEnv(envVar, envVarValue);
std::string cacheEntryName = cmStrCat("CMAKE_ECLIPSE_ENVVAR_", envVar);
cmValue 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,
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,
cmStateEnums::STRING, true);
mf->GetCMakeInstance()->SaveCache(lg.GetBinaryDirectory());
}
}
if (!valueToUse.empty()) {
out << envVar << "=" << valueToUse << "|";
}
}
void cmExtraEclipseCDT4Generator::CreateProjectFile()
{
auto const& lg = this->GlobalGenerator->GetLocalGenerators()[0];
cmMakefile* mf = lg->GetMakefile();
std::string const 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 (cmValue extraNaturesProp =
mf->GetState()->GetGlobalProperty("ECLIPSE_EXTRA_NATURES")) {
cmList extraNatures{ *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<cmSourceFile const*> 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 (auto const& lg : this->GlobalGenerator->GetLocalGenerators()) {
cmMakefile* makefile = lg->GetMakefile();
auto const& targets = lg->GetGeneratorTargets();
for (auto const& 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: {
char const* 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, std::string const& 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, std::vector<std::string> const& 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;
auto const& lg = this->GlobalGenerator->GetLocalGenerators()[0];
cmMakefile const* mf = lg->GetMakefile();
std::string const 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"); // noqa: spellcheck disable-line
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(
cmStrCat(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 (auto const& lgen : this->GlobalGenerator->GetLocalGenerators()) {
if (cmValue 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
cmValue cDefs =
mf->GetDefinition("CMAKE_EXTRA_GENERATOR_C_SYSTEM_DEFINED_MACROS");
if (this->CEnabled && cDefs) {
// Expand the list.
cmList defs{ *cDefs, cmList::EmptyElements::Yes };
// 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
cmValue cxxDefs =
mf->GetDefinition("CMAKE_EXTRA_GENERATOR_CXX_SYSTEM_DEFINED_MACROS");
if (this->CXXEnabled && cxxDefs) {
// Expand the list.
cmList defs{ *cxxDefs, cmList::EmptyElements::Yes };
// 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 (auto const& lgen : this->GlobalGenerator->GetLocalGenerators()) {
auto const& targets = lgen->GetGeneratorTargets();
for (auto const& 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");
cmList dirs{ 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");
cmList dirs{ 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();
std::string const& make = mf->GetRequiredDefinition("CMAKE_MAKE_PROGRAM");
std::string const& 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 (auto const& lgen : this->GlobalGenerator->GetLocalGenerators()) {
auto const& targets = lgen->GetGeneratorTargets();
std::string subdir =
lgen->MaybeRelativeToTopBinDir(lgen->GetCurrentBinaryDirectory());
if (subdir == ".") {
subdir.clear();
}
for (auto const& 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: {
char const* 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->GetObjectOutputRoot(), "\" \"",
cmSystemTools::GetCMakeCommand(), "\" -P \"");
cleanArgs += lgen->GetTargetDirectory(
target.get(), cmStateEnums::IntermediateDirKind::ObjectFiles);
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) {
char const* 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);
// noqa: spellcheck off
xml.EndElement(); // cconfiguration
// noqa: spellcheck on
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 (cmValue extraCProjectContents =
mf->GetState()->GetGlobalProperty("ECLIPSE_EXTRA_CPROJECT_CONTENTS")) {
fout << *extraCProjectContents;
}
xml.EndElement(); // cproject
}
std::string cmExtraEclipseCDT4Generator::GetEclipsePath(
std::string const& 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(
std::string const& 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(
std::string const& name, std::string const& type, std::string const& path)
{
return name + (type.empty() ? "" : "-") + type + "@" + path;
}
// Helper functions
void cmExtraEclipseCDT4Generator::AppendStorageScanners(
cmXMLWriter& xml, cmMakefile const& makefile)
{
// we need the "make" and the C (or C++) compiler which are used, Alex
std::string const& 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, std::string const& target, std::string const& make,
std::string const& makeArgs, std::string const& path, char const* prefix,
char const* 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, std::string const& profileID, bool openActionEnabled,
std::string const& openActionFilePath, bool pParserEnabled,
std::string const& scannerInfoProviderID,
std::string const& runActionArguments, std::string const& 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,
std::string const& name,
std::string const& path,
LinkType linkType)
{
char const* 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();
}