Building a CDT-based editor, Part 1: The C/C++ Development Tooling model

Exploring the classes that contain CDT information

The Eclipse C/C++ Development Tooling (CDT) project remains one of the most popular Eclipse downloads available. But the integrated development environment's (IDE's) richness makes the code difficult to understand and customize, which is an important concern for organizations attempting to integrate the CDT into their applications. This five-part "Building a CDT-based editor" series explains how the CDT editor works, and Part 1 introduces the data structures on which the CDT operates.

Share:

Matthew Scarpino, Java Developer, Eclipse Engineering, LLC

Matthew Scarpino is a project manager and Java developer at Eclipse Engineering LLC. He is the lead author of SWT/JFace in Action and made a minor but important contribution to the Standard Widget Toolkit (SWT). He enjoys Irish folk music, marathon running, the poetry of William Blake, and the Graphical Editing Framework (GEF).



12 September 2006

Also available in Japanese Vietnamese

I thought I'd sold my boss on the CDT, but he said there were just too many features. As it turned out, he only wanted the source editor -- the syntax coloring, automatic parsing, and especially the code completion. He asked if I could customize the editor and add it to our Rich Client Platform (RCP) application. "No problem," I said. "It's Eclipse! How hard could it be?"

Very hard, as it turned out. Currently, CDT V3.1 consists of 20 plug-ins that contain more than 100 packages and thousands of interfaces and classes. Figuring out the machinery turned into a long-term endeavor. But I eventually satisfied my boss with a working editor, and I'm happy to share what I've learned.

The CDT is arguably the finest open source C/C++ IDE available. In addition to full-featured editing, debugging, and indexing, it provides incredible flexibility in controlling the build process. If you don't want to write your own makefile, it creates one for you and updates it throughout your project. You can access the GNU Compiler Collection (GCC) and other GNU C/C++ applications within the CDT, but you can also integrate any other open source or commercial tool chain.

QNX Inc. proposed the CDT in 2002, and has taken the role of chief developer and maintainer. Much of the code is based on the Eclipse Java Development Tools (JDT), but QNX (in particular, Doug Schaefer) has added many capabilities, including advanced C/C++ parsing, the standard/managed make projects, and the configurable build process. If you'd like more information, look no further than the Eclipse CDT page or Doug Schaefer's well-written blog (see Resources).

The Bare Bones CDT

The goal of this series is to provide you with an understanding of CDT editing so you can build custom C/C++ tools. To make this process easier, I've created a stripped-down version of the full CDT that I call the Bare Bones CDT (BBCDT) (see Download for the code). I've removed the vast majority of the CDT's classes, but I've kept the same naming conventions -- partly to simplify the process of adding CDT code to the BBCDT, and partly because I'm lazy.

The BBCDT contains only two plug-ins: org.dworks.bbcdt.core (the Core plug-in) and org.dworks.bbcdt.ui (the user interface (UI) plug-in). The first plug-in provides the classes and interfaces that make up the CDT model. The second creates the UI, which includes the CEditor, its associated classes, and the wizards and pages needed to create new source files and projects.

For this first article, the BBCDT doesn't do anything interesting except show how the basic CDT classes interact. Subsequent articles explain how the CDT performs partitioning, syntax coloring, parsing, and code completion. These articles also expand on the BBCDT and make sure that these features are added to the code.


Elements of the CDT model

Regular Java™ code accesses files and directories through File objects. To perform Eclipse-specific operations on files in the workspace, however, you need the adapters in the Resource application program interface (API):

  • IFile
  • IProject
  • IFolder
  • IWorkspaceRoot

In addition to providing file access, these IResources give the workspace structure. The top-level resource is the IWorkspaceRoot, and its children are IProjects. Each IProject contains IFolders and IFiles. So far, so good.

The CDT model presents a different but similar set of adapters for workspace resources. Figure 1 shows their inheritance hierarchy.

Figure 1. The CDT model element hierarchy
CDT model element hierarchy

Figure 2 shows how the CDT uses these elements to structure its workspace. At the top, the ICModel contains ICContainers and ICProjects. ICProjects contain ISourceRoots, which function like JDT packages, but can contain code in different locations on the file system. To manage these locations, ISourceRoots contain ISourceEntry instances, each of which holds an IPath to a different source. Each ICElement has a specific name and a method to access its underlying IResource.

Figure 2. The CDT workspace structure
CDT workspace structure

ITranslationUnit

The ITranslationUnit represents a single file of C/C++ code and is the most important element in the CDT model. Each unit contains children that represent different aspects of a source code file, such as IInclude, IUsing, and INamespace. (I discuss the ITranslationUnit structure in much greater detail in future discussions on CDT parsing.)

In addition, each ITranslationUnit holds a single IWorkingCopy to manage unsaved changes to the code. Both the ITranslationUnit and the IWorkingCopy store their contents in separate IBufferCache instances, which are allocated by a BufferManager and function like a Least Recently Used (LRU) cache. This subject is involved, but if you're interested, look into the org.dworks.bbcdt.internal.core.util package.

Info objects

Each ICModel, ICContainer, ICProject, and ITranslationUnit also has a corresponding info object (CModelInfo, CContainerInfo, etc.). These info objects contain references to the element's children and provide access to them. If you wanted a list of ICProjects in the CModel, you'd call the CModel.getCProjects() method, which calls the CModelInfo.getChildren() method. The CDT creates an info object when the element is first opened and disposes of it when the element is closed.


Three stages of the CDT model

Now that you know the basic elements of the model, you need to know how they interact during CDT operation. The CDT model life cycle consists of three important steps: its initial creation, the creation of a CProject, and the creation of a TranslationUnit within the CDT editor.

CDT model elements and IAdapters

Like the resources that compose the Eclipse workbench, the CDT model elements (ICModel, ICProject, ITranslationUnit, etc.) are all descendants of the IAdaptable interface. This interface allows the CDT to add its specific functions to the existing classes without needing to subclass them. From a coding standpoint, an IAdaptable can be cast to another class by using its getAdapter(Class adapter) method.

For each element, the CDT provides an implementation class (CModel, CProject, TranslationUnit, etc.) with fully defined methods. However, by using the IAdaptable interface, you can create your own C/C++ elements without losing the existing capabilities of ICElements. That is, your new elements can adapt CDT elements in the same way that CDT elements adapt resources in the Eclipse workspace.

Step 1: Creating the CDT model

When the workbench initializes the CDT plug-ins, the Core plug-in creates a singleton CoreModel (not the CModel). This process in turn creates instances of two important classes: the PathEntryManager and the CModelManager. The PathEntryManager keeps track of the directories needed for the build process, such as included directories and macro libraries. In the CDT model, the path for each directory resides in a SourceEntry object.

But the CModelManager class is central to this discussion. It serves many important roles in the CDT:

  • Creates the CModel to serve as the top-level element in the element hierarchy
  • Adds and removes new model elements as their resources are created or deleted
  • Keeps track of model elements and their info objects
  • Holds a map of TranslationUnits and their WorkingCopy children
  • Stores a cache of currently opened elements
  • Responds to changes in resources' content types
  • Responds to changes in resources' descriptors

The most important functions are the first two. The CModelManager serves as the CDT model's factory and begins operation by creating the CModel object. Then it listens to resource changes in the workspace and updates the model whenever a CDT element is affected.

When any resource is created, deleted, or modified, the Eclipse workspace creates an IResourceDelta that stores the old and new hierarchical structure. The manager analyzes this with a DeltaProcessor, which determines whether the affected resource is a CElement. If so, it creates a new element corresponding to the resource and adds it to the parent's list of children.

The CModelManager function in the BBCDT performs all the above functions except the last two. A resource's content type is set by the <content-type> listing in the plugin.xml file, and a descriptor file is created to describe new projects.

Step 2: Creating a new CProject

The CDT provides a full extensible wizard structure for creating CDT resources. The New Project Wizard is particularly powerful, allowing you to configure every aspect of the project's build process, from the environmental variables to source indexing. When you complete the wizard, it and the Core plug-in perform four main tasks:

  1. Create and open an IProject at the given path
  2. Build an IProjectDescription to hold the IProject's general information
  3. Construct a CDescriptor to hold the CDT-specific information
  4. Give the IProject a CNature or CCNature, depending on the content type

The first and second steps are common for the creation of any IProject. In the second task, the IProjectDescription stores information that the workbench uses to define the project. This data is persisted in XML format within the .project file in the project's top-level directory. Two important elements are <buildSpec></buildSpec>, which lists the build commands for the project, and <natures></natures>, which marks the project as having characteristics beyond those of a regular IProject.

The CDescriptor in the third step is similar to the IProjectDescription. The main difference is that it contains data specific to the CDT and inserts this information within a separate .cdtproject file, which contains profiles for different tools used in the build process and specifies configuration parameters for each. This file uses an XML format similar to the IProjectDescription. Listing 1 shows an example of a .cdtproject profile declaration.

Listing 1. Profile information in the .cdtproject file
<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerFileProfile">
    <buildOutputProvider>
        <openAction enabled="false" filePath=""/>
        <parser enabled="true"/>
    </buildOutputProvider>
    <scannerInfoProvider id="makefileGenerator">
        <runAction arguments="-f ${project_name}_scd.mk" command="make"   
            useDefault="true"/>
        <parser enabled="false"/>
    </scannerInfoProvider>
</profile>

The last step involves tagging the project as a C or C++ project. The Core plug-in does this by adding either the CNature or CCNature to the IProjectDescription. These classes do nothing interesting by themselves, but when the DeltaProcessor recognizes the nature of a new project, it tells the CModelManager to create a CProject corresponding to the project resource and adds it to the CDT model hierarchy.

Step 3: Creating a new TranslationUnit and WorkingCopy

Unlike the CProject, the TranslationUnit isn't immediately constructed when its underlying IFile appears. Instead, it's created after the file is activated and the editor opens. To explain how this works, I'll start with the central class of the CDT editing process: the CEditor.

Essentially, the CEditor is a StyledText widget that fits into the workbench and gets content from an IEditorInput instance. In keeping with the Model-View-Controller (MVC) architecture, the Eclipse Text Editor API only uses this widget to provide the View aspect. The editor's information is encapsulated within an instance of IDocument. A SourceViewer acts as the controller, and the CSourceViewer manages access to the CDT document.

In the Core plug-in, plugin.xml holds contentType extensions for the different types of C/C++ files and associates each contentType with a file suffix. In the UI plug-in, plugin.xml associates the CEditor with these contentTypes. The BBCDT recognizes files with .bbc, .bbcpp, .bbh, and .bbhpp suffixes. When you create or double-click one of these files, the workbench converts the IFile to IEditorInput and uses it to initialize the CEditor.

As the editor is initialized, the CDocumentProvider performs three important tasks:

  1. It uses the input's IFile to create TranslationUnit and TranslationUnitInfo objects.
  2. It creates an IDocument for the editor with the information in the IEditorInput.
  3. It constructs the WorkingCopy for the TranslationUnit and its buffer.

To create the new CDT elements, the provider calls the CoreModel, which calls the CModelManager. These workspace operations, such as the CreateWorkingCopyOperation and DestroyWorkingCopyOperation, all implement the IWorkspaceRunnable interface. They run asynchronously and tell the workspace to prevent other operations from interfering with the resource modification.

Like a CProject, each TranslationUnit has its own configuration information, such as its annotations. However, the provider doesn't create a separate CDescriptor. Instead, it persists the data inside the IEditorInput's FileInfo object. This way, the next time the input is activated, the provider can access the unit's information without starting from scratch.


Running the BBCDT

I've provided the BBCDT as a plug-in and a plug-in project. Because the tool's main purpose is to provide a basis for further work, I recommend importing the project, instead of adding its capability to your Eclipse installation.

To keep the BBCDT as simple as possible, I left out the PathEntryManager, which means that the tool doesn't use SourceEntry objects or even SourceRoots. Therefore, you must add source files (.bbc, .bbh, .bbcc, and .bbhh files) directly to the project. To create BBCDT resources, I've created a new set of wizard/page classes in the org.dworks.bbcdt.ui.wizards package. To create a project, click File > New > Project and choose either the C or C++ option. To create a file, click New > Other and choose either the C or C++ option. Figure 3 shows what the BBCDT project and editor look like.

Figure 3. The BBCDT
The BBCDT

Conclusion

If you've accessed IFiles and IProjects in your Java code, the elements of the CDT model shouldn't present a problem. The CModel contains CProjects, which contain CSourceRoots. Each TranslationUnit corresponds to a single C/C++ source file, so the editor interacts with the CDT model by accessing and altering these elements.

With great power comes great complexity. This treatment may seem overly detailed, but you'll find it helpful if you start building your own C/C++ editor. You can get a much better idea of the CDT's mechanics by looking through the BBCDT source code. I recommend that you add, remove, and modify code to experiment with its operation.

The CDT model should also become clearer in subsequent articles, as I discuss the many ways the TranslationUnit and WorkingCopy interact with the CEditor and its UI classes. Part 2 goes further into how Documents manage events, how partitioning takes place, and how this partitioning provides for syntax coloring.


Download

DescriptionNameSize
Part 1 source codeos-ecl-cdt1.zip574KB

Resources

Learn

Get products and technologies

Discuss

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Open source on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Open source
ArticleID=156795
ArticleTitle=Building a CDT-based editor, Part 1: The C/C++ Development Tooling model
publish-date=09122006