1.1 Who Needs This Information
1.2 Managed Build System Overview
4.3 Dependency Makefile Fragments
4.4 Inter-Project Dependencies
5 Tutorial: An Example Tool Chain
5.1 Setting up your environment
5.2 Creating your plug-in project
5.9.2 Defined symbols and header file search paths
5.9.3 Built-in symbols and search paths
5.9.4 User-specified libraries and object modules
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.
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.
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.
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.
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.
Figure
2
Managed build model elements
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.
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 |
The example below shows a target definition called ‘Executable’. Tool and configuration information will be added to our definition is later sections.
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.
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 |
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 <…>’.
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.
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 |
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.
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.
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 |
The example below shows a configuration named Default that belongs to the target Executable.
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.
Attribute |
Description |
Required |
id |
The unique identifier of the tool this is a reference for. |
yes |
The example below shows how the user has overridden the compiler flags option in the compiler tool in the “Default” configuration.
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.
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.
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.
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.
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.
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.
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.
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.
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. |
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 |
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.
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.
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 |
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.
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.
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 |
The example below shows an option, Defined Symbols, which contains a pre-populated list of built-in values; "__I386__”, and "__i386__” respectively.
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.
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 |
The option below shows an enumerated option to flag the language dialect for the Gnu pre-processor.
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.
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
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.
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.
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.
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.
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.
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.
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.
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".
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.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.
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.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.
org.eclipse.cdt.example.toolchain
project in the Package Explorer to expand it. Double
click on the plugin.xml file to edit its contents.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.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.Now we will add a new target, configuration, and an example tool to the extension.
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.Example
Executable
for our target.org.eclipse.cdt.core.ELF
.
If you are running the tutorial on Window, enter the value org.eclipse.cdt.core.PE
.rm
–rf
.make
.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.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.
Test
Configuration
.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.
Compiler
.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
.both
from the list of choices.h,H
.-o
.ccc
as the value for the command property.We have now defined enough information to create a project for our new example target, so let’s go test it out.
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.
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.
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
.
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.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
.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.
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.
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.
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.
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.
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.
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.