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 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.
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):
IFileIProjectIFolderIWorkspaceRoot
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
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
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.
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.
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.
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
CModelto 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 theirWorkingCopychildren - 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:
- Create and open an
IProjectat the given path - Build an
IProjectDescriptionto hold theIProject's general information - Construct a
CDescriptorto hold the CDT-specific information - Give the
IProjectaCNatureorCCNature, 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:
- It uses the input's
IFileto createTranslationUnitandTranslationUnitInfoobjects. - It creates an
IDocumentfor the editor with the information in theIEditorInput. - It constructs the
WorkingCopyfor theTranslationUnitand 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.
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
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.
| Description | Name | Size | Download method |
|---|---|---|---|
| Part 1 source code | os-ecl-cdt1.zip | 574KB | HTTP |
Information about download methods
Learn
-
Check out the Eclipse CDT at Eclipse.org.
-
Read the fascinating blog of the leader of the CDT development team, Doug Schaefer.
-
Learn more about the Eclipse Foundation and its many projects.
-
For an excellent introduction to the Eclipse platform, see "Getting started with the Eclipse Platform."
-
Expand your Eclipse skills by visiting IBM developerWorks' Eclipse project resources.
-
Browse all of the Eclipse content on developerWorks.
-
Visit the developerWorks Open source zone for extensive how-to information, tools, and project updates to help you develop with open source technologies and use them with IBM's products.
-
Stay current with developerWorks technical events and webcasts.
Get products and technologies
-
Download the Eclipse CDT from Eclipse.org.
-
Check out the latest Eclipse technology downloads at IBM alphaWorks.
-
Innovate your next open source development project with
IBM trial software, available for download or on DVD.
Discuss
-
The Eclipse newsgroups offers many resources for people interested in using and extending Eclipse.
-
Get involved in the developerWorks community by participating in developerWorks blogs.
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).




