CDT Developers FAQ - temporary
Table of Contents

Intro

If you write Eclipse plug-ins that integrate with CDT http://www.eclipse.org/CDT on some level, you often need to access and/or manage the project's CDT configuration programmatically. Unfortunately, there's little documentation on the CDT build system's internals; also, Javadoc comments in the source are rare, and actually useful & up-to-date Javadoc comments are even rarer.

The purpose of this page is to collect some information in a FAQ form, to help developers get started with this task. If you come by some useful piece of information, please add it here!

This page is not part of the CDT Developers FAQ (http://wiki.eclipse.org/CDT/Developer/FAQ) because I didn't want to give the false impression that any of it is "official" information, but I'll be happy to contribute it to the FAQ if someone from the CDT Team reviews it and weeds out the mistakes.

If you want to contact me: a n d r a s omnetpp org, remove the spaces


Project, project description, build info

Accessing the build info

The starting point anything related to build info is ManagedBuildManager.getBuildInfo():

IProject project = ...;
IManagedBuildInfo buildInfo = ManagedBuildManager.getBuildInfo(project);

ManagedBuildManager is a central class with lots of static methods, most of them utility methods.

NOTE: ManagedBuildManager.getBuildInfo() actually takes IResource, but don't be fooled by that: it will return exactly the same object for any resource in the project (it only uses resources.getProject()). The argument should really be IProject.

IManagedBuildInfo can tell you all build-related information about the project, including configurations, pre and post-build steps, include paths etc.


How do I change the contents of the ".cproject" file?

There seems to be two way: (A) via the project description (cdt.core plug-in), and (B) via the managed build managed (cdt.build plug-in)

(A) Get the project description (ICProjectDescription) for the project,
modify the project description, and set it back on the project.

IProject project = ...;
ICProjectDescription projectDescription = CoreModel.getDefault().getProjectDescription(project);
// manipulate projectDescription...
CoreModel.getDefault().setProjectDescription(project, projectDescription);

TODO It is unclear what needs to be done for the changes to get reflected in IManagedProject and IManagedBuildInfo.

(B) Obtain the IBuildInfo from ManagedProjectManager, do changes on it, then invoke ManagedBuildManager.saveBuildInfo(IProject, boolean).

IProject project = ...;
IManagedBuildInfo buildInfo = ManagedBuildManager.getBuildInfo(project);
// change things in the buildInfo
ManagedBuildManager.saveBuildInfo(project, true);

This seems to be the preferred way.


How do I get an IProject?

That's basic platform stuff, handled by the resources plugin. For example, if you know the project name, you can write:

String name = ...;
IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(name);

How do I get an ICProjectDescription for my project?

Use CoreModel.getDefault().getProjectDescription(IProject).


What are IManagedProject, IManagedBuildInfo and ICProjectDescription, and how are they related?

IManagedBuildInfo seems to wrap an IManagedProject. I guess IManagedProjects don't exist independently (?).

TODO figure out relationship of IManagedBuildInfo vs ICProjectDescription. They look similar, e.g. both manage configurations (IConfiguration vs ICConfigurationDescription).

In any case, the first two are "build" stuff (managed by ManagedBuildManager, "cdt.build" plugin), and ICProjectDescription is "core" stuff (managed by CoreModel, "cdt.core" plugin).

TODO figure out how they are kept consistent…


Should I prefer ManagedBuildManager.saveBuildInfo(…) or CoreModel.setProjectDescription(…)?

Some comments from Mikhail Sennikovsky on cdt-dev on this:

"Actually you could use both methods. There is a slight difference between them though. With the first method the “real” configurations are accessed/modified directly and then applied, i.e. you are directly modifying configurations being used by the other parts of the CDT (e.g. the project builder), thus there is a risk that your modifications are made concurrently with accessing those settings by others (e.g. builder) making there behavior inconsistent. The second approach is more “safe” in that the modifications are being made to the configuration “copy” and then atomically applied to the entire system. So I would prefer using the second approach [i.e. setProjectDescription()]."


What are the MakeProject and ManagedMakeProject classes for?

They are probably dead code, left over from earlier CDT versions (at least they are not used anywhere). Should probably be removed.


Configurations

How do I access the active configuration, or all configurations?

CDT configurations (as in "Release", "Debug") are represented by IConfiguration. You can get the currently active configuration by calling IBuildInfo.getDefaultConfiguration():

IConfiguration activeConfig = buildInfo.getDefaultConfiguration();

NOTE: Don't confuse this with getSelectedConfiguration(), which is a UI thing (it returns the configuration last selected in the property pages).

Getting all configurations:

IManagedBuildInfo buildInfo = ManagedBuildManager.getBuildInfo(project);
IConfiguration configs[] = buildInfo.getManagedProject().getConfigurations())

How do I find configurations that generate executables for my platform?

The ManagedBuildManager returns a list of all configurations, but you will need to filter these if you want to generate executables for your current platform (e.g. if you're on Linux then you probably don't need the configurations for Cygwin, Mingw, or Mac OS X configurations that are returned by getExtensionConfigurations).
To this end, you can use the isPlatformOk method with the toolchain of a configuration. Additionally, a toolchain may be valid for the current platform, but not necessarily supported (e.g. on Windows both MinGW and Cygwin are valid toolchains, although they will be supported only if they are available). Use the isSupported() method to check that.
To filter the type of output of the toolchain (so-called "artefacts"), use the getBuildArtefactType method and compare the value with one of the constants in ManagedBuildManager (executable, static library, shared library).

As an example, the code below will typically find two configurations (one Debug and one Release, although not necessarily in that order) of the toolchain that generates executables for the current platform.

List<IConfiguration> cfgs = new ArrayList<IConfiguration>();
for (IConfiguration cfg : ManagedBuildManager.getExtensionConfigurations()) {
    IToolChain tc = cfg.getToolChain();
    if (tc != null && ManagedBuildManager.isPlatformOk(tc) && tc.isSupported()) {
        IBuildPropertyValue value = cfg.getBuildArtefactType();
        if (value != null) {
            if (ManagedBuildManager.BUILD_ARTEFACT_TYPE_PROPERTY_EXE.equals(value.getId())) {
                cfgs.add(cfg);
            }
        }
    }
}

How do I find out if a configuration is Debug/Release?

Get the "build type" property from your configuration, and check its type: it can only be "Debug" or "Release".

IBuildObjectProperties properties = cfg.getBuildProperties();
IBuildProperty property = properties.getProperty(ManagedBuildManager.BUILD_TYPE_PROPERTY_ID);
if (property != null) {
    IBuildPropertyValue value = property.getValue();
    if (ManagedBuildManager.BUILD_TYPE_PROPERTY_DEBUG.equals(value.getId())) {
        return true;
    }
}

What is the relationship of IConfiguration and ICConfigurationDescription? If I change one, does the other change as well?

ICConfigurationDescription represents configuration in CDT core level, and IConfiguration represents configuration in CDT build system level. While only one CDT core implementation exists, there may be a lot of different build systems (at least in theory). The existing Managed Build System is only one among other ones.

ICConfigurationDescription, in principle, does not know about build-system specific features implemented by IConfiguration instances. So we should change "common" properties in ICConfigurationDescription, and build system-related properties in IConfiguration implementor. When setProjectDescription() called, both changes would be applied.


How to get IConfiguration from ICConfigurationDescription, or the other way round?

One way to get IConfiguration is ManagedBuildManager.getConfigurationForDescription(ICConfigurationDescription). What this method really does is unclear.

TODO ICConfigurationDescription from IConfiguration


Accessing and changing settings

How do I write a property page that edits CDT project configuration?

In the property page, you can obtain a working copy of ICProjectDescription from CDTPropertyManager. This working copy is shared among all property pages that display or edit CDT configuration. At the end of the dialog session, you can ask CDTPropertyManager to save the working copy, or you can do it yourself via CoreModel's setProjectDescription().

Thus:

  • In the createContents() method of your property page, obtain the ICProjectDescription working copy with CDTPropertyManager.getProjectDescription(this, getProject()). This call also doubles as registering your page in CDTPropertyManager, note the this parameter. (CDTPropertyManager has to recognize when one dialog session ends (the dialog closes) so that for the next session it can serve up a fresh working copy. It does that by keeping track of the property pages that registered, and adding a dispose listener to them so it can notice when the count goes back to zero. Maybe not the most straightforward way to do it, but unfortunately Eclipse's property dialog doesn't offer a dialog session storage facility.)
  • Later on in the dialog, you can obtain the working copy again any time you want to work with it (read or modify it) via CDTPropertyManager.getProjectDescription(getProject()) (the this is no longer needed). Or, you can remember the reference from the call in createContents(); the two are equivalent.
  • In setVisible(), if visible==true, update the page controls because other pages may have modified the ICProjectDescription working copy.
  • In performOk(), call CDTPropertyManager.performOkForced(this);. This will end up calling CoreModel.getDefault().setProjectDescription() with the working copy. There is also a CDTPropertyManager.performOk(this), but it doesn't save the working copy if it thinks there's no need to, and apparently it is wrong sometimes, so I recommend performOkForced() that always saves it (or manually calling setProjectDescription() on CoreModel).

How do I get CDT to re-read the discovered project and toolchain-specific include paths and macros?

The following code seems to work for invalidating the discovered info:

IManagedBuildInfo buildInfo = ManagedBuildManager.getBuildInfo(project);
if (buildInfo != null)
    for (IConfiguration config : buildInfo.getManagedProject().getConfigurations())
        CfgDiscoveredPathManager.getInstance().removeDiscoveredInfo(project, new CfgInfoContext(config));

There is also MakeCorePlugin.getDefault().getDiscoveryManager().removeDiscoveredInfo(project) which looks like it's supposed to do the job, but when I tested it it had no effect.

How do I find out if a project uses Managed Make or Standard Make?

On the UI this corresponds to the "Generate Makefiles automatically" checkbox on the "C/C++ Build" page of the project properties dialog.

In the ".cproject" XML, you will find a managedBuildOn="true/false" attribute on the <builder> element within the <configuration> elements. (Note that this is a per-configuration setting!!!)

Programmatically, the same value is returned by the IConfiguration.isManagedBuildOn() method. Example:

IProject project = ...;
IConfiguration activeConfig = ManagedBuildManager.getBuildInfo(project).getDefaultConfiguration();
boolean isManagedMake = activeConfig.isManagedBuildOn();

You may want to check this setting in all configurations not only on the active one.


How do I switch a project from Managed Make to Standard Make or vica versa?

Use the IConfiguration.setManagedBuildOn(boolean) method. You may want to change only the active ("default") configuration, or all configurations. To save the changes, call ManagedBuildManager.saveBuildInfo(IProject, boolean).

IProject project = ...;
IConfiguration activeConfig = ManagedBuildManager.getBuildInfo(project).getDefaultConfiguration();
activeConfig.setManagedBuildOn(true);  // or false
ManagedBuildManager.saveBuildInfo(project, true);

How do I set the indexer settings on a Standard Make project?

Unfortunately there is only one internal class to do this: IndexerPreferences

// activate project specific settings
IndexerPreferences.setScope(project, IndexerPreferences.SCOPE_PROJECT_PRIVATE);

Properties props = new Properties();
props.put(IndexerPreferences.KEY_INDEXER_ID, "org.eclipse.cdt.core.nullindexer");
props.put(IndexerPreferences.KEY_INDEX_ALL_FILES, "true");
//        props.put(IndexerPreferences.KEY_FILES_TO_PARSE_UP_FRONT, "hugo.h");
//        props.put(IndexerPreferences.KEY_SKIP_ALL_REFERENCES, "false");
props.put(IndexerPreferences.KEY_SKIP_TYPE_REFERENCES, "false");

IndexerPreferences.setProperties(project, IndexerPreferences.SCOPE_PROJECT_PRIVATE, props);

CCoreInternals.savePreferences(project);

Attention: The indexer id used in the preferences is not the id of the "CIndexer" extension point but the id of the "IndexerPage" extension point.


How do I set the build command on a Standard Make project?

Use IConfiguration's methods: setBuildCommand(String), setBuildArguments(String), then save it via ManagedBuildManager.saveBuildInfo(). A slightly different (but apparently more or less equivalent) way is to get an IBuilder using IConfiguration.getEditableBuilder(), and use IBuilder's setter methods. Still needs to be saved afterwards.

IProject project = ...;
IConfiguration configuration = ManagedBuildManager.getBuildInfo(project).getDefaultConfiguration();
configuration.setBuildCommand("gmake");
configuration.setBuildArguments("-f Makefile.mine");
ManagedBuildManager.saveBuildInfo(project, true);

Again, this only changes the active configuration; you may want to iterate over and change all configurations.

Another way via project descriptions (the "core" model). (Based on code from Oleg Krasilnikov):

IProject project = ...;
ICProjectDescription projectDescription = CoreModel.getDefault().getProjectDescription(project);
IConfiguration configuration = ManagedBuildManager.getConfigurationForDescription(projectDescription.getActiveConfiguration());
configuration.setBuildCommand("gmake");
configuration.setBuildArguments("-f Makefile.mine");
CoreModel.getDefault().setProjectDescription(project, projectDescription);

How do I set the build directory on a Standard Make project?

The build directory (which is found on the "C/C++ Build" page in the project properties dialog) is the working directory when "make" is invoked.

The the managed builder API, it can be accessed via IConfiguration.getEditableBuilder():

IConfiguration configuration = ...;
String buildPath = ...;
configuration.getEditableBuilder().setBuildPath(buildPath);

Then save the changes via ManagedBuildManager.saveBuildInfo(..).

In the "core model", the same can be accessed via the following classes: ICProjectDescription —> ICConfigurationDescription —> ICBuildSetting:

ICConfigurationDescription configDesc = ...;
IPath buildPath = ...;
configDesc.getBuildSetting().setBuilderCWD(buildPath);

Don't forget to save the project description afterwards (CoreModel.getDefault().setProjectDescription(…)).


How do I know which folders are marked as source folders in my project?

Use IConfiguration.getSourceEntries() (so, source folders are per configuration!). This method returns an array of ICSourceEntry objects; each one corresponds to a source folder tree, and contains the list of resources that have been excluded from build. Any folder which is outside all source folders also counts as excluded.

Note: the name of ICSourceEntry.getFullPath() is misleading, it actually returns project-relative path!


How do I know if a folder or file is excluded from build?

Resources can be excluded per configuration. The sources entries (ICSourceEntry) in the configuration store whether the resource is excluded. Exclude patterns (*,?) are also supported.

Some confusion arises about how ICSourceEntry stores/returns exclusion entries. getExclusionPatterns() returns IPaths which are relative to the source folder; fullExclusionPatternChars() returns character arrays (!!!) which represent project-relative paths.

Two families of utility functions can be use to check whether a resource is excluded:
CoreModelUtil.isExcluded(…), and CDataUtil.isExcluded(…). The former can only be used if the resource is not null, is under the source folder etc; the latter does these checks for you then delegates to the former. Thus, the latter, CDataUtil.isExcluded(…) is recommended for general use.

The following code checks if the given resource is excluded from build:

IResource resource = ...;
IConfiguration configuration = ...; // see FAQ above
ICSourceEntry[] sourceEntries = configuration.getSourceEntries();
boolean isExcluded = CDataUtil.isExcluded(resource.getProjectRelativePath(), sourceEntries);

How do I access the include paths?

The Include paths are displayed on the UI at properties -> "C/C++ General" / "Paths and symbols" page -> "Includes" tab, and can be specified per language (in addition to per-configuration and per-resource).

They are accessible via the CDT Core model. In nutshell: CoreModel -> ICProjectDescription -> ICConfigurationDescription -> ICFolderDescription -> ICLanguageSetting -> ICLanguageSettingEntry. The ICLanguageSettingEntry you get in the end will also be an ICIncludePathEntry.

Along the way you'll need to know which configuration you want (the active one?), which folder (the project root?), which language (i.e. assembly/C/C++ — this is tricky, see later), and that the "kind" of the ICLanguageSetting we want is ICSettingEntry.INCLUDE_PATH.

Now the code example:

IProject project = ...;
ICProjectDescription projectDescription = CoreModel.getDefault().getProjectDescription(project);
ICConfigurationDescription activeConfiguration = projectDescription.getActiveConfiguration(); // or another config
ICFolderDescription folderDescription = activeConfiguration.getRootFolderDescription(); // or use getResourceDescription(IResource), or pick one from getFolderDescriptions()
ICLanguageSetting[] languageSettings = folderDescription.getLanguageSettings();
ICLanguageSetting lang = ...; // pick one from languageSettings, by id
ICLanguageSettingEntry[] includePathSettings = lang.getSettingEntries(ICSettingEntry.INCLUDE_PATH);

What to do with entries in the ICLanguageSettingEntry[] array you get:

  • toString() prints everything important about the entry — very useful
  • getValue() will tell you the path as string: "C:/mingw/include/c++/3.4.5", "/myproject/myfolder"
  • (getFlags() & ICSettingEntry.VALUE_WORKSPACE_PATH) tells you whether the value string is to be interpreted as a workspace path or a filesystem path
  • isBuiltIn() tells you whether it's a compiler system path ("C:/mingw/include", "/usr/bin", etc). isBuiltIn() is equivalent to (getFlags() & ICSettingEntry.BUILTIN).
  • there are other flags as well that you might be interested in

Getting back to language IDs: it's tricky because they're subject to subclassing. "GNU C++" is "org.eclipse.cdt.core.g++", but MSVC C++ likely has a different ID, so you may want to check for some superclass language ID (or check if the name contains "C++" as substring, but that's dodgy…).

Another way is to query the source content type of the language setting, and compare to one of the built-in types in CCorePlugin:

String[] contentTypeIds = setting.getSourceContentTypeIds();
if (contentTypeIds.length > 0 && CCorePlugin.CONTENT_TYPE_CSOURCE.equals(contentTypeIds[0])) {
  // ok this language setting is valid for C source
}

How do I access the library paths?

Much the same way as the include paths, but use ICSettingEntry.LIBRARY_PATH as the "kind" constant.


How do I access the macros (preprocessor symbols)?

Much the same way as the include paths, but use ICSettingEntry.MACRO as the "kind" constant. In the ICLanguageSettingEntry objects, getName() will be the macro name and getValue() the macro value.


How do I add new source folders (and other entries) to my project?

To add a new source folder, the simplest is to create a new CSourceEntry explicitly:

ICSourceEntry entrySrc = new CSourceEntry(folderSrc, null, ICSettingEntry.RESOLVED);

Other source types have corresponding concrete classes that are available as part of the API.
Alternatively, you can use the generic method below:

CDataUtil.createEntry(int kind, String name, String value, IPath[] exclusionPatterns, int flags)

CDT Nature

How do I add CDT Nature and Managed Build to an existing project? (method 1)

If you have an existing project type and want to add CDT capabilities & features to it, you can try the code below in your plug-in:

CProjectNature.addCNature(fProject, mon);
ICProjectDescriptionManager mgr = CoreModel.getDefault().getProjectDescriptionManager();
ICProjectDescription des = mgr.getProjectDescription(fProject, true);

if (des != null)
        return; // C project description already exists

des = mgr.createProjectDescription(fProject, true);

ManagedBuildInfo info = ManagedBuildManager.createBuildInfo(fProject);
IProjectType projType =
         ManagedBuildManager.getExtensionProjectType("my.project.type"); // or get projectType from UI
IToolChain toolChain =
         ManagedBuildManager.getExtensionToolChain("my.toolchain"); // or get toolChain from UI

ManagedProject mProj = new ManagedProject(fProject, projType);
info.setManagedProject(mProj);

IConfiguration[] configs = ManagedBuildManager.getExtensionConfigurations(toolChain, projType);

for (IConfiguration icf : configs) {
    if (!(icf instanceof Configuration)) {
        continue;
    }
    Configuration cf = (Configuration) icf;

    String id = ManagedBuildManager.calculateChildId(cf.getId(), null);
    Configuration config = new Configuration(mProj, cf, id, false, true);

    ICConfigurationDescription cfgDes =
    des.createConfiguration(ManagedBuildManager.CFG_DATA_PROVIDER_ID,
                                         config.getConfigurationData());
    config.setConfigurationDescription(cfgDes);
    config.exportArtifactInfo();

    IBuilder bld = config.getEditableBuilder();
    if (bld != null) { bld.setManagedBuildOn(true); }

    config.setName(toolChain.getName());
    config.setArtifactName(fProject.getName());

}

mgr.setProjectDescription(fProject, des);

Of course, you have to replace "my.project.type" and "my.toolchain" with the appropriate CDT project type and toolchains, respectively.

There's still one caveat, though. You will notice the code directly instantiates an instance of Configuration which is inside an internal package of CDT. Unfortunately, as of now there's no "valid" way to create it with the same parameters as we did. We will have to request an addition to the API to support this.


How do I add CDT Nature and Managed Build to an existing project? (method 2)

This method does the same thing as the first method, but without access to the internal API. Basically I just spent a lot of time examining what the code of method 1 does, and tried to find the equivalent in the API (when possible or necessary).

CProjectNature.addCNature(project, mon);
ICProjectDescription des = CoreModel.getDefault().createProjectDescription(project, false);

// create build info and managed project
IConfiguration cfg = cfgs.get(0);
ManagedBuildManager.createBuildInfo(project);
IManagedProject mProj = ManagedBuildManager.createManagedProject(project, cfg.getProjectType());

for (IConfiguration icf : cfgs) {
    String id = ManagedBuildManager.calculateChildId(icf.getId(), null);

    // clone the configuration and set the artifact name
    IConfiguration config = mProj.createConfiguration(icf, id);
    config.setArtifactName("${ProjName}");

    // creates/add the configuration to the project description
    des.createConfiguration(ManagedBuildManager.CFG_DATA_PROVIDER_ID, config.getConfigurationData());

    // set the builder to "managed" mode
    IBuilder bld = config.getEditableBuilder();
    if (bld != null) {
        bld.setManagedBuildOn(true);
    }
}

CoreModel.getDefault().setProjectDescription(project, des);

Toolchains

How do I add a new toolchain?

Add a org.eclipse.cdt.managedbuilder.core.buildDefinitions extension to your plugin.xml, and create a toolChain element in it. You can subclass an existing toolchain, and specify an OS list as filter. You can specify an error parser, environment variable provider, macro provider, scanner discovery class, and tons of other stuff.

Gotchas with the macro provider, scanner discovery and similar classes: it looks like you are not supposed to access the project configuration (ICProjectDescription) in their code, otherwise bad things may happen. (Apparently, those classes are invoked during initialization of the project configuration info when not everything is available yet. In my code, I accessed the source entries via ICConfigurationDescription.getSourceEntries(), and got a default setting (project root with no exclusion), apparently because the real one was not yet available. Even worse, the default value was then remembered by ICConfigurationDescription, and also returned from later getSourceEntries() calls, so it appeared as if CDT forgot the source entries.)


How does CDT decide whether MinGW, Cygwin or some other toolchain is supported on the computer?

For each toolchain, one can specify a class implementing IManagedIsToolChainSupported in the plugin.xml, and its isSupported() method decides. For MinGW and Cygwin these classes are MingwIsToolChainSupported and IsGnuCygwinToolChainSupported (this is also in use, despite an obsolete comment claiming it is not).

What the code actually does is best to look up in the source, but at CDT 4.0.2 it roughly looks like this:

MingGW

MinGW is supported if the MinGW "bin" directory can be found. The "bin" directory is located in the following way:

  1. Does the <platform-install-dir>/mingw/bin directory exist? if so, use that. Else:
  2. Does the <platform-install-dir>/../mingw/bin directory exist? if so, use that. Else:
  3. Does the "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\MinGW\InstallLocation" registry key exist, and does it contain the name of a directory that has a "bin" subdirectory? If so, that is used, else:
  4. Does "C:\MinGW\bin" exist? Use that if so, otherwise no bonus.

Cygwin

To detect Cygwin, the code first it tries to find the "etc" directory of the Cygwin installation, then it checks whether all required packages are installed.

  1. Finding the "etc"directory is done by checking the registry. "[HKLM|HKCU]\SOFTWARE\Cygnus Solutions\Cygwin\mounts v2" is checked (via launching regedit.exe!)
  2. Checking required packages: The <etc>/setup/installed.db file is supposed to contain lines starting with the following texts: "gcc", "binutils", "make".

Can I add a tool that produces C/C++ source code?

As of CDT 4.0.2, this cannot be gotten to work, due to the following bug: https://bugs.eclipse.org/bugs/show_bug.cgi?id=211858.

A workaround is to bypass CDT, i.e. write your own incremental builder and add it to the platform (org.eclipse.core.resources.builders extension point). Your builder should run before the CDT builder, so that the CDT builder will see the source files your builder created. Of course this is suboptimal — if you can do something so that the above bug gets fixed, please help.


Can I add new tools to a toolchain?

Currently the only way to add tools to toolchains is to "subclass" them in plugin.xml. (See Managed Build Extensibility Document.) This is problematic because:

  1. You have to subclass from each and every toolchain individually (resulting in toolchains like "Mingw GCC with lex/yacc", "Cygwin GCC with lex/yacc", "Linux GCC with lex/yacc", "Solaric GCC with lex/yacc" etc)
  2. Toolchains subclassed into different directions don't mix. If your team adds the bison/flex tools, and my team adds swig support, then a third team wanting to use both in the same project is still doomed: they HAVE TO choose between "MinGW GCC with lex/yacc" and "MinGW GCC with SWIG"!

This is insane. In addition to subclassing, CDT should also allow 3rd party plugins to contribute tools to existing toolchains. Same applies to macro providers etc.

See https://bugs.eclipse.org/bugs/show_bug.cgi?id=212127, and related discussions (or rather, monologues) on the cdt-dev mailing list.

If you agree that this is an important issue and should be fixed in CDT asap, then please make your voice heard in the bugtracker (add comments to #212127) and/or the cdt-dev mailing list. Thanks!


Accessing source files and the index

How do I obtain the parsed translation unit for a source file?

Get ICProject from CoreModel for the given project, and call findElement() with project relative path. It will return an ITranslationUnit (actually the return type is ICElement, which you need to cast).

ICProject cproject = CoreModel.getDefault().getCModel().getCProject(sourceFile.getProject().getName());
ICElement element = cproject.findElement(sourceFile.getProjectRelativePath());  // this apparently throws exception for non-source files instead of returning null
ITranslationUnit unit = (ITranslationUnit)element;  // not clear whether we need to check for null or instanceof

ITranslationUnit gives you access to the full AST of the source file. The getAST() method returns the full syntax tree, and there are also specific utility methods that return things like the list of #includes (getIncludes()), namespaces (getNamespaces()), "using" directives (getUsings()) etc. in the file.

Note that generally the CoreModel doesn't keep the parse trees of source files in memory. Getting an ITranslationUnit itself is a cheap operation, but if you call any method on it that needs the AST, it will likely trigger parsing of the file. If you want to avoid parsing, you may get away by using the index (it contains far less information than the AST.)


How do I obtain the index?

You can get the index from the index manager:

ICProject cproject = ...; // see above
IIndexManager manager = CCorePlugin.getIndexManager();
IIndex index = manager.getIndex(cproject);

However, you'll likely want to read from the index, do you need to obtain (and later release) a read lock, something like this:

IIndex index = ...;
try {
    index.acquireReadLock();
    // use the index
    ...
}
catch (InterruptedException e) {  // from index.acquireReadLock()
    ...
}
catch (Exception e) {
    ...
}
finally {
    index.releaseReadLock();
}

How do I find a file in the index?

You can find a file in the index with the getFile()/getFiles() method. Files must be specified to the index using IIndexFileLocation objects that store the file's workspace path or URL. A file has one view in the index for each "linkage"; getFile() returns one for a particular "linkageId". It is not entirely clear what a linkage is or how one can obtain a linkageId, but luckily the getFiles() method returns the indexer's view for all linkages.

IFile file = ...;
IIndex index = ...;
IIndexFileLocation fileLocation = IndexLocationFactory.getWorkspaceIFL(file); // this one is for IFile; see also other "get" methods
IIndexFile[] indexFiles = index.getFiles(fileLocation);  // for all "linkages"; if result is empty, the file is not indexed (maybe not even exists)

// example: get list of #includes for the first linkage
if (indexFiles.length > 0) {
    IIndexFile indexFile = indexFiles[0];
    IIndexInclude[] includes = index.findIncludes(indexFile);
    ...
}

How do I refresh/recreate the index, and check the state of indexing?

The index manager has methods for rebuilding the index (reindex()), refreshing the index for certain projects/folders/files, etc. These methods just schedule the indexing, the indexing will actually run in a background job. You can use isIndexerIdle() to figure out if the indexer has already done everything it was asked to, or still working / has some work to do.


Given a function name, how do I find where it is defined and open it?

This method will query the Indexer to find all *definitions* of a function within a project.

public void findFunctionInSource(IProject  project,  String  functionName)  {
  List files = new ArrayList() ;
  ICProject cProject = CoreModel.getDefault().getCModel().getCProject(project.getName());
  IIndexManager manager = CCorePlugin.getIndexManager();
  try {
    index = manager.getIndex(cProject);
    index.acquireReadLock();
    IBinding[] bindings = index.findBindings(functionName.toCharArray(), IndexFilter.ALL, null);
    for (int  binding = 0;  binding < bindings.length; binding++)  {
      if  (bindings[binding] instanceof IFunction)  {
        IFunction  ifunction = (IFunction)  bindings[binding];
        IIndexName[] names = index.findNames(ifunction, IIndex.FIND_DEFINITIONS);
        for  (int j = 0; j < names.length; j++)  {
           IIndexFile  file = names[j].getFile();
           if  (file != null)  {
              IIndexFileLocation filelocation = file.getLocation();
              files.add( filelocation.getFullPath());
           }
           IASTFileLocation location = names[j].getFileLocation();
           if  (location != null)
             int  offset = location.getNodeOffset();
       }
    }
  } catch  (Exception  e)  {
    //..
  } finally  {
    if  (index  !=  null)
      index.releaseReadLock();
  }
}

Other sources of info

Readings


Where can I find examples of programmatic manipulation of the build configuration?

You can find some samples of config setting in CDT Property pages UI.

They are located in 2 places:

  • org.eclipse.cdt.managedbuilder.ui.properties (for MBS-specific settings)
  • org.eclipse.cdt.ui.newui (for CDT core settings).

All classes representing Property pages context have "*Tab" name (ex.: ErrorParsTab.java)

For example, changing build command is performed in org.eclipse.cdt.managedbuilder.ui.properties/BuilderSettingsTab.java, and clean build enabling is made on neighbour tab: org.eclipse.cdt.managedbuilder.ui.properties/BuildBehaviourTab.java.

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License