Table of Contents

1 Introduction. 2

1.1 Who Needs This Information. 2

1.2 Managed Build System Overview.. 3

1.3  The Standard Build System.. 3

2 Build Model Elements. 4

2.1 Model 4

2.2 Target 4

2.2.1 Schema. 6

2.2.2 Example. 6

2.3 Tool 7

2.3.1 Schema. 8

2.3.2 Example. 8

2.4 Option Category. 9

2.4.1 Schema. 9

2.4.2 Example. 9

2.5 Configuration. 10

2.5.1 Schema. 10

2.5.2 Example. 10

2.6 Tool Reference. 10

2.6.1 Schema. 10

2.6.2 Example. 11

2.7 Options. 11

2.7.1 Option Types. 11

2.7.2 Default Values. 12

2.7.3 Option Commands. 13

2.7.4 Schema. 13

2.7.5 Example. 14

2.8 Option Reference. 14

2.8.1 Schema. 14

2.8.2 Example. 15

2.9 List Option Value. 15

2.9.1 Schema. 15

2.9.2 Example. 15

2.10 Enumerated Option Value. 16

2.10.1 Schema. 16

2.10.2 Example. 16

3 UI Representation. 17

3.1 New Project Wizard. 17

3.2 Build Property Page. 18

4 Makefile Generator 21

4.1 Main Makefile. 23

4.2 Makefile Fragments. 24

4.3 Dependency Makefile Fragments. 25

4.4 Inter-Project Dependencies. 25

5 Tutorial: An Example Tool Chain. 26

5.1 Setting up your environment 26

5.2 Creating your plug-in project 27

5.3 Creating the extension. 27

5.4 Adding a target 27

5.5 Adding a configuration. 28

5.6 Adding a tool 28

5.7 Testing the target 29

5.8 Adding tool options. 29

5.9 Taking the next step. 30

5.9.1 Adding more tools. 30

5.9.2 Defined symbols and header file search paths. 31

5.9.3 Built-in symbols and search paths. 31

5.9.4 User-specified libraries and object modules. 31

5.9.5 Target hierarchies. 32

1         Introduction

C and C++ developers are a diverse group. The tools they use, the processes they follow, and the level of support they expect from their development environments vary widely. The CDT provides a framework for integrating those tools into Eclipse and the managed build system is part of that framework. Understanding how the managed build system works, and what steps are required to extend it is the focus of this document.

1.1      Who Needs This Information

The information in this document describes the design of the managed build system and discusses how to add new build targets and tools to it through the ManagedBuildInfo extension point. It is intended for someone who wants to understand how the managed build system works, or is interested in adding their own tool chain specification to it.

 

The CDT comes configured to generate makefiles to build executables, static libraries, and shared libraries on Cygwin, Linux, and Solaris using Gnu tools. If you are using the CDT on those platforms and have access to Gnu tools, then you do not need to modify anything. Please feel free to skip sections 2, 3, and 5 as they are primarily concerned with adding new tool chains to the build model.

 

If you are working with tools other than Gnu, or you wish to build for targets the CDT does not support out of the box, then you have to decide whether you will provide your own makefile and use the standard builder, or add a description of your target to the extension point and let the CDT generate the makefiles for your project.

 

If you choose to add your own tools to the managed build system, it is assumed that you are familiar with XML and the Eclipse extension point mechanism. Having made the standard disclaimer, it should be said that the tutorial in section 5 presents a cookbook approach to adding a new tool specification, so you can always jump right in and refer to the appropriate documentation if you get stuck.

1.2      Managed Build System Overview

The managed build system consists of four main components; a grammar to specify tool chains for platforms, a build model that manages build information for projects, a UI that allows users to modify the build information, and a makefile generator that creates makefiles based on that information. The diagram below shows the basic set of relationships between these elements.

Figure 1 Managed build system architecture

In addition, the build model supplies the CDT parser with information about the search path for includes files and the defined preprocessor symbols.

1.3      The Standard Build System

As you know, there is also a “standard” build system supplied as part of the CDT framework that is unrelated to the managed build system. The standard system provides a small set of tools to build a user’s projects. The user is expected to supply a makefile which includes enough information to build their project. The UI allows the user to switch between targets defined in the makefile, like clean or all, and for the user to enter the information the parser requires.

 

The decision to use the standard or managed build system is a trade-off. For users with an existing project that already has a set of working makefiles, or for users that prefer to write their own makefile, the standard system may be perfect. However, many users are uncomfortable writing makefiles, so the standard system may present a barrier to adoption for them. The standard system is not extensible, so users cannot integrate their own tool chains.

2         Build Model Elements

The managed build system defines a grammar to describe tool chain information. This information is used to store invariant data, like the command line invocation for a specific compiler, for example. The build system also stores user settings between sessions, like the level of debugging information that is needed for a particular build configuration. The following section describes the format of the grammar and what the information is used for by the build model.

2.1      Model

Figure 2 Managed build model elements

2.2      Target

In its current implementation, a “target” in the build model is really a high-level concept used to gather together the configurations and tools that are required to build something. Another way to look at targets is to consider that tools transform one type of file into another, and since targets can contain many tools, they are a way of organizing the steps needed to transform source files into a build goal.

 

Since Eclipse runs on a limited number of platforms, what a target builds may not run on the same platform as Eclipse and the build tools. Certain targets should not even be available to users if they running Eclipse on a platform where the build tools are unavailable. Keeping these concepts in mind, the following section describes the attributes of a target.

 

Targets can be arranged into hierarchies to promote the efficient sharing of tools and configurations. If you have defined a target that should not be selected by the user, but is a root for other targets, it may be declared abstract by setting the isAbstract attribute to ‘true’. Abstract targets do not appear in the UI. You must provide a unique identifier for the target in the id attribute. The build model uses this information to distinguish between the target definitions it finds. Children of the abstract target will have the same tools and configurations the abstract target has. For these children to function properly, their parent attribute must contain the unique identifier of the parent target.

 

A concrete target must have at least one configuration defined for it. A target must also define (or inherit) a set of tool definitions that work together to produce the build goal as an output. You must also provide a meaningful name that will be displayed to the user in the UI and new project wizards.

 

The target defines the command needed to invoke the make utility in the makeCommand attribute. Any special flags that need to be passed to the make utility are defined in the makeFlags attribute. The command to remove files on the host machine is defined in the cleanCommand attribute.

 

Typically a build target will only be valid on a limited subset of operating systems. For example, it does not make much sense to allow a user to create a Solaris shared library project if they are running Eclipse and the CDT on Windows. You can specify the operating systems that the target is restricted to as a comma-separated list in the osList attribute. At the moment, you can specify “win32”, “linux” and “solaris” as the filters.

 

The CDT offers a facility for parsing binary files if it knows which output format the build artifact has been produced with. The binaryParser attribute must contain the id of the appropriate parser if you want build artifacts of the target to be parsed in the workspace. There are just two defined binary parsers; ”org.eclipse.cdt.core.PE” for Windows artifacts, and “org.eclipse.cdt.core.ELF” for Linux and Solaris. This information is used to set the parser when a project is created and is not something the user can change through the UI.

 

The target is responsible for maintaining the name of the final build goal. The user selects the name of the build target in the UI, and the target maintains it in the artifactName attribute. The implementer of a tool chain should not specify this in the plug-in manifest. However, the default extension for the target can be specified using the defaultExtension attribute.

2.2.1      Schema

Attribute

Description

Required

artifactName

The name of the build goal defined by the target. This is set by the user through the UI and stored in the build model through this attribute.

no

binaryParser

The id of the appropriate parser for the build artifact.

yes

cleanCommand

The command to remove files on the build machine. You must define this value if the target does not have a parent, or it is not defined in the parent.

yes

defaultExtension

The extension the build goal will have, for example ‘.exe’ or ‘.so’.

no

id

A unique identifier that the model manager will use to keep track of this specific target.

yes

isAbstract

Flags the target as abstract. An abstract target can not be selected by the user in the UI, but children of the target will inherit its tools and configurations.

yes

isTest

A target can be flagged for test purposes only. It can be manipulated programmatically, in JUnit tests for example, but not selected by the user in the UI.

yes

makeCommand

The command to invoke the make utility. You must define this value if the target does not have a parent, or it is not defined in the parent.

yes

makeFlags

The default flags passed to the make utility on the command line.

yes

name

The name for the target that is displayed to the user in the UI.

yes

osList

A comma-separated list of operating systems that the target is valid for.

no

parent

The unique ID of the parent of the target.

no

2.2.2      Example

The example below shows a target definition called ‘Executable’. Tool and configuration information will be added to our definition is later sections.

2.3      Tool

A tool represents some sort of executable component that can take a set of inputs and produce a set of outputs. A tool must have a unique id for the build model, and a name that is displayed to a user through the UI.

 

Certain tools logically belong to certain kinds of projects. For example, the Gnu compiler is invoked differently for C and C++ source files. You can specify a filter for a tool based on the nature of a project using the natureFilter attribute. When a new C project is created, a “cnature” is added to it. New C++ projects have both a “cnature” and “ccnature”. The build model interprets the filter as follows. If you specify a ‘cnature’ filter, then the tool will only be displayed if the project has a “cnature” and does not have a “ccnature”. If you specify a ‘ccnature’ filter, then the tool will be displayed if the project has a “ccnature”. The default if no filter is specified is to display the tool for all projects.

 

A tool can be defined as part of a target, or as an independent specification. If a tool is target specific, it can only be used to build for that target. However, if a target is defined as abstract, the children of that target will be able to use the tool. For target-independent tools, a tool reference must be created in a target in order to use it as part of a build.

 

Tools can define a set of input file extensions in the sources attribute. This indicates that a tool will build for those and only those file types. Similarly, a tool might specify a set of file extensions that they will produce in the outputs attributes.

 

Each tool specifies a command that will be placed in the makefile during the makefile generation stage of building. Two optional flags control how the command is generated. If the tool requires a special output flag, such as ‘-o’ for a compiler or linker, the implementer must specify that in the outputFlag attribute. If the output of the tool usually has a special prefix, like the prefix ‘lib’ for libraries on POSIX systems, the implementer must specify this in the outputPrefix attribute.

 

One of the clients of the information in the build model is the managed builder. It must track the dependencies between elements in the workspace, and to do that, it needs to know if a file is a header or a source file. Currently, the build model uses the list of file extensions specified in the headerExtensions attribute to identify a file as containing an interface.

2.3.1      Schema

Attribute

Description

Required

id

A unique identifier for the tool that will be used by the build model.

yes

name

Human-readable name for the tool to be used in the UI.

yes

sources

A comma-separated list of file extensions that the tool will produce output for.

no

outputs

The extension that the tool will produce from a given input.

no

command

The command that invokes the tool. For example, gcc for the Gnu C compiler, or g++ for the Gnu C++ compiler.

yes

outputFlag

An optional flag for tools that allow users to specify a name for the artifact of the tool. For example, the GCC compiler and linker tools typically allow the user to specify the name of the output with the '-o' flag, whereas the archiver that creates libraries does not.

no

outputPrefix

Some tools produce files with a special prefix that must be specified. For example, a librarian on POSIX systems expects the output to be lib.a so 'lib' would be the prefix.

no

dependencyCalculator

Unused in 1.2

no

headerExtensions

A comma-separated list of file extensions that are used for header files in by the tool chain.

yes

natureFilter

Specify the tools for which a tool will work.

yes

2.3.2      Example

The tool shown in the example below will appear in the UI with the label Compiler. It will be used to build any file in the project with a ‘.C’ extension and will produce a file with an ‘.o’ extension. When the makefile is generated, a rule will be generated with the command ‘g++ <…> -o <…>’.

2.4      Option Category

A tool can have a large number of options.  To help organize the user interface for these options, a hierarchical set of option categories can be defined. A unique identifier must be specified in the id attribute. This will be used by the build model to manage the category. The user will see the value assigned to the name attribute. If the category is nested inside another category, the unique identifier of the higher-level category must be specified in the owner attribute, otherwise use the identifier of the tool the category belongs to.

2.4.1      Schema

Attribute

Description

Required

id

Used by the build model to uniquely identify the option category.

yes

name

A human-readable category name, such as 'Preprocessor Options'. This will be the name the user sees displayed in the UI.

yes

owner

Option categories can be nested inside other option categories. This is the ID of the owner of the category.

yes

2.4.2      Example

This example shows an option category that will be displayed in the UI with the label Flags. There are two options defined in this category, General, and Optimization.

2.5      Configuration

We have seen that a target defines the information about the tools needed to build a project for a particular environment. Configurations are used to pre-define and store the settings that the user specifies for those tools.

 

A target must have at least one default configuration defined for it. Users can create new configurations for project, but they must be based on the settings defines in a default configuration. For example, a user may want to create a “Profile’ configuration based on the target’s default ‘Debug’ configuration.

 

Each configuration must have a unique identifier specified in the id attribute that will be used by the build model to manage the configuration. It must also have a name that will be displayed in the UI in the build property page and new project wizards.

2.5.1      Schema

Attribute

Description

Required

id

A unique identifier that the model manager will use to keep track of this specific configuration.

yes

name

The human-readable name that will be displayed in the UI to identify this configuration.

yes

2.5.2      Example

The example below shows a configuration named Default that belongs to the target Executable.

2.6      Tool Reference

A tool reference is primarily intended to be used when saving user settings between sessions. When the user has overridden an option in the referenced tool, an option reference with the new setting is created and added to the tool reference.

 

Tool references are used by the build model for two distinct tasks; because they contain option references, tool references hold onto user settings between sessions. They can also be added to a default configuration specification if the default settings for the tool should be overridden. For example, a ‘Debug’ configuration may have optimization disabled by default, whereas a ‘Release’ configuration may default to the highest possible level.

2.6.1      Schema

Attribute

Description

Required

id

The unique identifier of the tool this is a reference for.

yes

2.6.2      Example

The example below shows how the user has overridden the compiler flags option in the compiler tool in the “Default” configuration.

2.7      Options

Options in the build model are used to organize and maintain the command arguments that are sent to tools during the build. Users interact with the build model through the UI to set the value of options. Options hold different kinds of values, so there are some subtle, yet important rules for how options are to be defined. These rules are summarized in Table 1.

 

Each option must have a unique id for the build model to properly manage it. A descriptive name that will appear in the UI must be specified. Options can be organized into categories to keep the UI more manageable. If an option category has been defined for the tool, and the option should be displayed as part of that category, then the unique identifier of the option category must be specified in the category attribute.

2.7.1      Option Types

Some options contain commands to turn a feature off or on, such as setting a flag to see descriptive messages from a tool. Others contain lists of values, such as a set of directories to search for files. Still others are a single selection from a pre-determined range of choices, like the level of debugging information to produce, or the type of architecture to build for. The valueType attribute is used to indicate to the build model what kind of option it is.

 

Specifying the type of value an option contains is an important design decision, since it controls how the build model treats the contents of the option’s attributes, and just as importantly, how the option is displayed to the user. The basic types are string, boolean, stringList, and enumerated. There are also three specialized cases of list options, includePath, definedSymbols, and libs, to manage the list of paths to search for header files, the defined preprocessor symbols, and the libraries to link against respectively.

2.7.1.1  String Options

An option of type ‘string’ is simply that; a string containing a set of values the user has typed in the UI. When the UI is created, it will display the option using a simple entry widget. This option type is useful for options that cannot be easily specified using lists or enumerations, or for options that are not frequently set. For these types of options, the build model will ignore what it finds in the command attribute.

2.7.1.2  Boolean Options

An option of type ‘boolean’ is used to specify an option that is either true or false. The option will be displayed to the user as a check box. The value of the option is set true by selecting the check box, and false by deselecting it. If true, the command associated with the option will be passed to the tool when it is invoked. The default value of the option will be considered when it is displayed in the UI.

2.7.1.3  Enumerated Options

Enumerated options are displayed in the UI in a combo-box. With enumerated options, the option definition takes on an organizational role; the important information is stored in the enumerated option values. Any information specified in defaultValue is ignored, since the contents of the enumerated value definitions are used to populate the selection widget. The option answers the command of the selected enumerated value, so any information in command is also ignored.

2.7.1.4  List Options

String list options are displayed in the UI using a list control and a button bar that allows users to add, remove, and reorder list items. Elements of the list are defined and stored in list options values, as described in section 2.9. Like enumerated options, lists ignore the information in the defaultValue attribute, but unlike the enumerated option, they treat any pre-defined list option values as defaults. The value defined in the command attribute will be applied to all the values in the list.

 

2.7.1.4.1   Special List Options

There are four special cases of string list options; “includePaths” specify the paths to search for header files, “definedSymbols” for user-defined preprocessor defines, “libs” for libraries that must be linked into the final build goal, and “userObjs” for external object files that must be linked.

 

While specifying these types of options as type stringList will make them appear in the UI correctly, the build model will not be able to recognize them as special in any way. Since certain functions of the CDT require this information to function correctly, it is important to flag these types of options appropriately. For example, the search and indexing function may not perform correctly if the includes paths and defined symbols are not set for a project. Similarly, the makefile generator will not be able to properly generate a build target if it is unaware of the libraries and external object files that participate in the final build step.

2.7.2      Default Values

Options can contain default values that apply to the option until the user has edited them through the UI. You can specify those values using the defaultValue attribute. However, the type of option will determine how the build model treats the value it finds associated with the attribute. Options that define simple string values will pass the value to the tool exactly as it is defined in the attribute. For Boolean options, any value but the string ‘true’ will be treated as false. List options treat all the defined list option values as default, and enumerated options search through the defined enumerated values for the default.

2.7.3      Option Commands

The values stored in the options are passed to build tools with unique flags, depending on the compiler and the option. For example, an option defining the paths a linker should search for libraries might contain a large number of search paths, but each path is passed to the linker with a ‘-L’ flag. The command attribute is used to hold the actual flag to pass along with the option value.

 

The build model handles the value it finds associated with the command attribute differently depending on the type of value the option is managing based on the following heuristic. For string options, the command is ignored since the contents of the option are treated as the command. For enumerated options, the command associated with the selected enumerated value is used, not the command defined in the option. For Boolean options, the command is used if the option value is set to true, otherwise it is ignored. For list options, the command is applied to each element of the list.

Table 1

Option Value Type

Uses Default Value

Uses Command

UI Element

‘string’

Yes

No

Entry widget

‘boolean’

Yes

Yes if true, else no

Check box

‘enumerated’

No.

No.

Combo-box

‘stringList’

No.

Yes.

List and button bar.

2.7.4      Schema

Attribute

Description

Required

id

A unique identifier for the tool that will be used by the build model.

yes

name

Human-readable name for the tool to be used in the UI.

yes

valueType

Type of value the option contains.

yes

value

Overridden value assigned to the option by the end user.

yes

category

This is the id of the option category for this option. The id can be the id of the tool which is also a category.

no

defaultValue

Optionally specifies the value for the option if the user has not edited it. For options containing a Boolean value, the string ‘true’ is treated as 1, any other value as 0.

no

command

An optional value that specifies the actual command that will be passed to the tool on the command line.

no

2.7.5      Example

The example below shows the specification for the optimization level option for a compiler. Note that it is an enumerated type, so the only attributes defined for the option itself are its id for the build model, a human-readable name, the id of the category it belongs to, and the type of value the option holds.

2.8      Option Reference

An option reference always belongs to a tool reference, and is used in two ways. First, the build model uses option references to hold onto information the user has changed through the UI and to store it between sessions. The second is to override the default option settings in a configuration.

 

The reference identifies the option it overrides through the id attribute. The defaultValue attribute is used to hold onto the user entry, but it is used differently depending on the valueType of the option. The attribute contains the strings ‘true’ or ‘false’ for Boolean options. String options contain the data entered by the user. For enumerated options, the attribute contains the selected enumerated list value. For list options, this attribute is not used. Instead, listOptionValues are used.

2.8.1      Schema

Attribute

Description

Required

id

The unique identifier of the option that this is a reference to.

yes

defaultValue

For boolean and string options, this field is used to hold the value entered by the user. For enumerated options, it is used to hold the selected enumerated option value. For list options, this attribute is not used.

no

command

unused in 1.2

no

2.8.2      Example

The example below shows how the build model saves overridden option information in the project file. In this case, the tool reference is a linker, and the option references are for linker flags and library paths.

2.9      List Option Value

Some options are best described using a list of values. This build model element is used to define an individual element of a list option. Typically, these options are populated by the user, not by the person describing the option. However, if you define one or more values in your extension point, they will be displayed in the UI when the user edits the build settings for the project. If the user modifies those settings, the overridden values will be stored by the build model and displayed in the UI.

 

There is an exception to this, however. Certain core functions in the CDT rely on the built-in parser to function correctly. In order to return accurate values, the CDT parser must mimic (as closely as possible) the preprocessor that ships with the tool chain used by the target. Unfortunately, these tools often have a number of built-in symbols and include paths that the user is never required to set, and may be unaware even exist. In those cases, the implementer of the tool chain must set those values in the tool definition and flag them by setting the value of the builtIn attribute to true. Built in list option values are never shown to the user, and are only passed to clients of the build model that specifically request them.

2.9.1      Schema

Attribute

Description

Required

builtIn

An optional Boolean field that tells the build model to treat the value defined in the as read-only.

no

value

The contents of the list item. The build model will apply the flag defined in the option to each value in the list.

no

2.9.2      Example

The example below shows an option, Defined Symbols, which contains a pre-populated list of built-in values; "__I386__”, and "__i386__” respectively.

2.10Enumerated Option Value

Some options are best described as a single selection from a list of choices. For example, users typically select the level of optimization they want the compiler to apply when creating a build artifact. The enumerate option value is used to define the elements of the list of choices.

 

Each element of an enumerated option has a name that will be shown to the user in the UI. It also has a command which should correspond to the command line option that gets passed to the tool by the builder if this element is selected.

 

A default element can be indicated by setting the value of isDefault to ‘true’. If the user has not overridden the selection in the UI, the default element will be displayed. If no default is specified, the first element in the list is assumed to be the default and is displayed to the user.

2.10.1 Schema

Attribute

Description

Required

id

A unique identifier for the tool that will be used by the build model.

yes

name

A descriptive name that will be displayed to the user in the UI as one of the option values to select.

yes

isDefault

Flags this enumerated value as the default to apply to the option if the user has not changed the setting.

no

command

The command that the enumerated value translates to on the command line.

yes

2.10.2 Example

The option below shows an enumerated option to flag the language dialect for the Gnu pre-processor.

3         UI Representation

In addition to controlling the way a project is built, the build model also defines how the user interface will appear. There are two principle ways a user interacts with the build settings model; the first is at project creation time through the New Project wizards. The second is through the build settings property page.

3.1      New Project Wizard

The new relies on the target and configuration settings from all specified tool chains to populate list of choices it presents to the user. The figure below shows how the list of targets is populated with any target whose isTest and isAbstract attribute are set to “false”. The target name is used to populate the selection widget. Similarly, the configuration check list is populated with all the defined configurations associated with the selected target.

Figure 3 New project wizard

3.2      Build Property Page

The contents of build property page for a project are created by examining the tools, option categories, and options defined for the current configuration and target. In this section we will look at how the user interface interprets the information in the build model to display options to the user.

 

The configuration information pane of the build property page consists of two combo-boxes. The first is populated with a list of all targets that apply to the project. The second contains a list of configurations that are defined for the target currently selected in the first. The figure below shows a project targeted solely at a Cygwin executable with two configurations; ‘Release’ (not shown), and ‘Debug’. Note that the build settings model is queried for the target and configuration name information.

Figure 4 Configuration selection

Users change the build settings for options associated with categories and tools. The UI relies on the information in the build settings model for that information. The figure below shows how the tool list, displayed in a tree view, is populated. Tools are the root elements of the tree. Categories are displayed as leaves of the tool they belong to. In both cases, the name defined in the plug-in manifest is used as the text of the tree elements. Note that the tool uses an externalized string to identify its name to help internationalize a tool specification, but this is not necessary.

Figure 5 Tools and option category display

As mentioned in the discussion of the build settings model, options know what type of data they manage. Different option types require different UI widgets to properly represent that data to the user. The figure below shows what UI elements are created for each type of option.

 

The ‘Compiler Flags’ option contains a string option. In this example, the option is intended to be the place the user enters all those “extra” flags that are not defined anywhere else in the property page. Options containing strings display their contents in a simple entry widget.

 

The “Optimization Level” option is an enumerated option. These types of options force the user to select a single value from a list of possible choices. Note that the name of the option is applied to the label in the UI, whereas the name of each individual enumeratedOptionValue element is used to populate the list.

 

The “Include Paths” option is a special case of a stringList option. The contents of this option left undefined in this example, so the user sees an empty list. However, all list options are displayed in a list control with an associated button bar for adding, removing, and reordering list elements. Note that the optionType attribute is set to “includePath”. This notifies the build system that it must pay special attention to the values entered in this option. There are clients of this information in the CDT that will query the build system for this information, and this is currently the only way to flag these values as special.

Figure 6 Option display

Finally “Verbose”, a Boolean option, is displayed as a check-box. Since the default value for this option is defined as “false”, the check-box is left unselected when it is created.

 

Note that the UI actually builds itself “on the fly” based on the options descriptions in the plug-in manifest. The order of the options is the basis of the page layout. If the layout is not satisfactory, you must edit the plug-in file itself. You must restart the workspace after editing the manifest for your changes to take effect in the UI.

4         Makefile Generator

The third key element of the managed build system is the makefile generator. The makefile generator is one of the key clients of the information stored in the build settings model. The best way to understand how the makefile generator works is to look at a real project. The figure below shows the project that we will be using for the purposes of this discussion. The source for the project is spread over the directories source1/, source2/, and source2/source21. Header files are located in 2 locations; headers/, and source2/source21.

Figure 7 Example project source files

While simple, this example illustrates some of the problems projects using make typically face when source files are organized hierarchically. One approach to these types of problems is to generate a makefile for each subdirectory, then call make recursively, culminating in the final build step which, in theory, brings all of the build results together.

 

The problem with managing this type of approach lies in understanding the dependencies and handling them properly when the makefiles are generated. In order for this to happen, all the dependencies have to be properly specified and complete. As long as there are no dependencies between resources in different subdirectories, the makefiles in a recursive approach will contain a properly partitioned set of dependencies. However, in a more realistic project organization, the fragmentary makefiles will have incomplete representations of the dependencies. In order to correct for this, we would have to do some of the work that make gives us for free.

 

The approach the makefile generator takes is to use a single makefile to build the entire project. To keep the makefile manageable and readable, the makefile generator creates makefile and dependency file fragments for each subdirectory of the project that contributes source code, and uses the include capability of make to bring them all together.

 

The figure below shows the makefile, makefile fragments, and dependency fragments that are generated for the project.

Figure 8 Generated makefiles

In the next sections, we will examine the makefiles that are generated in more detail.

4.1      Main Makefile

There is one main makefile generated for a project. Based on information for the target, the proper clean command is defined as a macro. Note that for efficiency, the contents of macros are calculated only when they are defined or modified, thus all assignment operators are generated as ‘:=’ or ‘+=’ with the exception of the list of objects.

 

The makefile defines the macros that hold the list of build sources, but they are populated in the makefile fragments. It also contains a list of subdirectories that contribute source files to the build. The makefile generator will generate fragmentary makefiles for each of the directories, so the main makefile must include each of these fragments.

This makefile is passed as an argument to make, so it contains the real build target, along with clean and all targets. Finally, the makefile generator will calculate dependencies for each of the source files in the build, and generate these into a dependency fragment for each subdirectory. The main makefile includes each of the fragments as well.

4.2      Makefile Fragments

Obviously, the makefile we just looked at is incomplete. There are no rules for building actual source files, and no source files listed. However, the makefile generator places that information into makefile fragments for each subdirectory contributing source to the build. The figure below shows what the fragment for the source1/ subdirectory looks like.

The fragment contributes one file, class1.cpp, and a rule to build all source files with the ‘cpp’ extension. The content of the dependency and command lines is derived from the build settings model. For the dependency line, the makefile generator asks the build model if there are any tools that build files with a particular extension. If so, the tool is asked for the extension of the output. For the command line, the tool that builds for the extension supplies the actual command, while the options for the tool supply the arguments to pass to it.

4.3      Dependency Makefile Fragments

There is one final piece to the puzzle, and that is a list of dependencies for each source file in the build. Recall that make will rebuild any file that is out of date in its dependency graph, but it only adds the dependency to the graph if it is explicitly told to do so. Thus, it is the responsibility of the makefile generator to completely describe all dependencies for make. Consider the dependencies of the final build target to Class1, as shown in the graph above. We can see that the make will need to rebuild Class1.o if Class1.cpp, Class1.h or Class2.h changes. In the makefile fragment, we have only defined a dependency between files with an ‘o’ and a ‘cpp’ extension.

 

The makefile generator places the remaining, explicit dependencies in a separate makefile fragment for each subdirectory. The figure below shows the fragment for the source1/ subdirectory.

4.4      Inter-Project Dependencies

A project may reference one or more additional projects in the workspace. The makefile generator attempts to generate these dependencies in two ways. First, the makefile must have a dependency on the build goal of the referenced project in the main target, and it must include instructions for building those targets as a separate rule.

 

For the remainder of this discussion, let us consider the following basic scenario. Project A builds an executable, a.exe. It references project B which builds a library libB.a. The main build target in the makefile for project A would be generated with the output of project B as a dependency.

As you can see from the generated makefile above, the rule for the target A.exe will be evaluated if the output of B has changed. This works well if the output of project B can be determined. However, that is only the case when project B is managed. Standard make projects do not know what the output of their build step is since that information is encoded in the makefile. If project A references a standard project, it will not have an explicit dependency on the output of that project.

 

The second element of the inter-project dependency is the rule to build the dependent project. This is generated as part of the deps target to ensure that the output of B is up-to-date when A is built. The rule to build the referenced project is simply a command to change to the appropriate build directory of the referenced project and call make again. Note that $(MAKE) will evaluate to the same invocation that was used to build the main project including the flags that were passed to it.

5         Tutorial: An Example Tool Chain

New managed build system tool chains are specified by extending the ManagedBuildInfo extension point defined in the org.eclipse.cdt.managedbuilder.core plug-in. The easiest way to do this is to create a new plug-in project and add your own definitions there.

5.1      Setting up your environment

If you are starting with a clean environment, you will need import the plug-ins org.eclipse.cdt.core, org.eclipse.cdt.make.ui, and org.eclipse.cdt.managedbuilder.ui (and any plug-ins they require) into your run-time workbench instance by performing the steps below. If you already have the required plug-ins in your workbench instance, proceed to the section "Creating your plug-in project".

  1. From the resource perspective, use File > Import... > External Plug-ins and Fragments.
  2. Continue clicking on the Next > button until you get to the screen called Selection. This screen will contain a list of all of the plug-ins in your host workbench instance.
  3. Select org.eclipse.cdt.core, org.eclipse.cdt.make.ui, and org.eclipse.cdt.managedbuilder.ui from the list and then click the button Add Required Plug-ins.
  4. Click on the Finish button. Your Navigator view should contain the selected plug-ins and all of the plug-ins they require.

5.2      Creating your plug-in project

Technically the extension can be defined in any plug-in manifest, but for this tutorial we will create a new, empty plug-in project to start.

  1. You will need to create a project to add your tool chain definition. The simplest way is to open the New Project... wizard (File > New > Project...) and choose Plug-in Project from the Plug-in Development category. This will set up your project with a default plug-in manifest file. Use org.eclipse.cdt.example.toolchain as the name for your project. By default, the wizard sets org.eclipse.cdt.example.toolchain as the id as well. We are going to be defining the tool chain in the plug-in manifest file without writing any code, so choose Create a blank plug-in project on the Plug-in Code Generators page and click on the Finish button.
  2. When asked if you would like to switch to the Plug-in Development perspective, answer Yes.

5.3      Creating the extension

You have added the required plug-ins to your workspace instance and you have a brand new project with an empty manifest file. We are now ready to add our tool chain definition to the managed build system by extending the ManagedBuildInfo extension point.

  1. Double click on the org.eclipse.cdt.example.toolchain project in the Package Explorer to expand it. Double click on the plugin.xml file to edit its contents.
  2. We have to add a dependency between our project and the org.eclipse.cdt.managedbuilder.core plug-in where the extension point is defined. Click on the Dependencies tab located along the bottom of the manifest editor. Click the Add… button located beside the Required Plug-Ins list. Select org.eclipse.cdt.managedbuilder.core from the list and then click the Finish button.
  3. Select the Extensions tab located along the bottom of the manifest editor. Click the Add… button located beside the All Extensions list. Make sure that Generic Wizards is selected in the left-hand list, and Schema-based Extensions from the right, and then click the Next > button.
  4. You should now be on the Extension Selection page. Make sure that the Show only extension points from the required plug-ins check-box is selected. Select org.eclipse.cdt.managedbuilder.core.ManagedBuildInfo from the list of extension points. Use org.eclipse.cdt.example.toolchain as the Point ID for the extension, and Example Tool Chain for the Point Name. Click the Finish button.

5.4      Adding a target

Now we will add a new target, configuration, and an example tool to the extension.

  1. Right click on org.eclipse.cdt.managedbuilder.core.ManagedBuildInfo to access the context menu. Select New > target to add a target definition. A new target named org.eclipse.cdt.example.toolchain.target[n] should appear below the extension point. Right click on the new target to access the context menu and select Properties to open the properties editor for the new entry.
  2. Let’s give the new target a better name. Locate the name property in the Properties browser and click on the row to edit the value of the property. For now, let’s use the name Example Executable for our target.
  3. Set the value of the binary parser property based on the platform you be using to create your example projects on. For example, if you are running this tutorial on Linux or Solaris, enter the value org.eclipse.cdt.core.ELF. If you are running the tutorial on Window, enter the value org.eclipse.cdt.core.PE.
  4. Now set the clean command for the target. For the purposes on this example, click on the cleanCommand property to edit it and enter rm –rf.
  5. Do the same for the make command. Locate the makeCommand property, click on it to edit the value, and enter make.
  6. We want the new target to appear when we run the new project wizard on our host platform, so we have to define the operating systems that the target should be visible on. Locate the osList property and click it to edit the value. Enter win32 if you are running the tutorial on Windows, linux if you are running on one of the Linux distributions, or solaris if you are running on a version of Solaris.

5.5      Adding a configuration

Now we have added a basic target definition. We now want to define a default configuration. Normally you would consider defining at a release and debug configuration, but we want to keep this example simple so we will restrict ourselves to a single configuration.

  1. Right click on Example Executable in the All Extensions list. From the context menu select New > configuration. Click on the new configuration to bring up its properties in the property browser.
  2. Click on the name property and edit the value to be Test Configuration.

5.6      Adding a tool

We could now run the new project wizard and create a new managed project based on this target, but before we do that, let’s define a tool for the target.

  1. Right click on Example Executable to get the context menu and select New > tool. Give the tool the name Compiler.
  2. Tools declare which file extensions they operate on and, optionally, the file extensions they produce. Our imaginary compiler only works on files with a ‘c’ of ‘C’ extension. Locate the sources property and set its contents to be a comma-separated list containing c,C. Note that there should not be any spaces between the values. Let us assume that the output of the compiler is an object module that has the extension ‘o’. Set the value of the outputs property of the tool to o.
  3. Let us assume that the tool should appear for both C and C++ projects, although this is not always the case. Locate the natureFilter property and select both from the list of choices.
  4. The build model needs to know if there are any special file extensions that indicate a file is a ‘header’ file. Set the headersExtension property to be a comma-separated list containing h,H.
  5. Tools often have a flag to specify the output of tool. For the purposes of this example, set the outputFlag property to -o.
  6. Finally, we want to specify the command that is needed to invoke the tool. For this example, we are not interested in actually calling a real tool, so just enter ccc as the value for the command property.

5.7      Testing the target

We have now defined enough information to create a project for our new example target, so let’s go test it out.

  1. Make sure our example project is selected in the Package Explorer. Select Run > Debug As > Run-time Workbench to start a new run-time workbench instance that includes the new tool information you have created.
  2. In the new workspace, open the C/C++ Development Perspective.
  3. Run the new project wizard. From the Selection page choose either a managed C or C++ project. Click the Next > button, give your project any name you wish, and click Next > again. Note, if the wizard does not display a next button, you have probably forgotten to specify the make and clean commands. You will have to add this information to the tool chain definition and restart your debugging session.
  4. You should now be at the Select a Target page. Your new target will appear as a choice in the Platform selection widget. Select it and note that the list of available configurations now contains the single configuration we defined for the target. Click Finish.
  5. Right click on your new project in the Navigator or C/C++ Project view to access the context menu, and select Properties to open the property browser for the project. Select C/C++ Build from the choices and note that the tool we defined appears in the list.

At this point, you have no doubt noticed that the property page does not have any way to edit the settings for the tool. That is because we have not defined any options yet. It’s time to edit the tool chain definition again.

5.8      Adding tool options

Users expect to be able to change the settings for their build tools through the property page for a project. What they see is controlled by the way options are defined in the tool chain specification. We will create an option category, and then add two example options to it.

  1. Switch back to the Plug-in Development perspective. Right click on the Compiler entry in the extension description to bring up the context menu. Select New > optionCategory to add the category. Set the name of the category to General.
  2. You must specify the id of the tool the category belongs to in the owner property. The simplest way to do this is to copy the id from the compiler and paste it into the owner property of the category. Click on the Compiler entry to open its properties. Right click on the id property as though you were going to edit it. Instead of typing, hit ctrl-c. Switch back to the option category, right click the owner property and hit ctrl-v.
  3. Set the unique id of the category to anything you want.
  4. Right click on the tool, not the category, to bring up its context menu and select New > option to add our first option. Name the option Include paths and set the valueType property to includePath from the list of choices.  Please refer to section 2.7 for a description of value types and options. In the command property, enter I. In the category property, put the unique id of the category that you entered in step 3.
  5. Add another option to the compiler. Set its name to Other flags. Set its valueType to string, its category to be the id you created in step 3, and its defaultValue to -c.

 

At this point, you can test how your options appear in the UI by debugging your run-time workbench. You should see something like this.

Figure 9 Property page with tool and category

There are other types of options not discussed in this tutorial, so feel free to add options to your tool definition to get a feel for how they appear in the UI.

5.9      Taking the next step

The purpose of the tutorial you just followed was to become familiar with the steps involved in creating a simple tool chain, and to get a feeling for how the choices you make in the specification of options effect the UI. In this section, we will discuss some additional points that you need to consider before specifying your own tool chain.

5.9.1      Adding more tools

Unless you just happen to have a compiler on your system that is invoked with ‘ccc’, the example tool we created is not going to build anything. Further, the tool we defined transforms source files into object files. Another tool, like a linker, would be needed to transform those object files into a final build goal. For most targets, defining a compiler and “something else” is usually sufficient, but you may have to define additional tools if your tool chain requires intermediate build steps to function properly.

5.9.2      Defined symbols and header file search paths

There are elements of the CDT core that require build information to function properly. Things like the indexing service, search, or code-assist will only function correctly if the built-in parser can retrieve information about the paths to search for include files and the preprocessor symbols defined for the project. The build model only promises to store the type and value of an option, it does not know anything about the contents. However, you can flag certain options as special so the build model will know to pay special attention to them. As the implementer of the tool chain, you should make sure your specification has options of type “includePaths” and “definedSymbols”. The build model will pay special attention to these options and answer them to the appropriate clients in the CDT core without any further intervention on your part.

5.9.3      Built-in symbols and search paths

Every compiler relies on having a correct set of preprocessor symbols and header file search paths to perform proper builds. Even compiler from the same vendor may use different symbols and search paths when building for different operating systems. Some of these values may be defined by the user, but others will be built into the tools themselves so the user will be unaware of them. Unfortunately, the CDT parser needs to know about the entire set to properly parse source files.

 

There are two approaches you can take, but both involve pre-populating the include path and defined symbol options with list option values containing the correct information. If you add a value to the include path or symbol option, it will be displayed to the user by default. This may be the right approach to take if you believe that users will change these values frequently. However, it will clutter the UI with values and since they are editable, users may delete them accidentally.

 

The alternative is to flag the list option value as a built-in value. In this case, the user will not be able to edit the values through the UI. This has the advantage of keeping the UI cleaner, but the only way for the user to edit these values if something changes is to directly edit the plug-in manifest where the extension is specified.

 

The CDT team is currently developing a mechanism to specify this information in an extensible way. In the current release however, we are relying on the implementers of a tool chain to supply the default symbols and paths in their specification. Please refer to section 2.9 for more details on specifying list option values.

5.9.4      User-specified libraries and object modules

Similarly, a user may want to specify external libraries to link against in the final build step. The build model needs to be told to pay special attention to an option containing libraries so that when the makefile generator requests them, it can answer a valid list. Flag the option value type as “libs” for external libraries or “userObjs” for object modules.

5.9.5      Target hierarchies

One area of the build model that the tutorial does not touch on is the concept of abstract targets discussed in section 2.2.1. It would be quite time consuming, not to mention error prone, if you had to redefine common tools or properties each time you wanted to create a new target. Instead, the build model allows you to organize targets into hierarchies that promote the sharing of common property settings and tools between related targets. When you create a parent target though, you may not want that target to be selected by the user in the new project wizard. In that case, make the target abstract and it will no longer appear as an option for users. Flagging a target as abstract is a UI design decision; you can declare a non-abstract target as the parent of another target. You just have to be sure that you want the user to be able to create a new project based on the parent as well as the child.