The XCDL.py Definitions Reference
Contents
- 1 Introduction
- 2 Mandatory properties
- 3 The option object
- 4 The component object
- 5 The package object
- 6 The interface object
- 7 The activeIf property
- 8 The category property
- 9 The children property
- 10 The computed property
- 11 The defaultValue property
- 12 The headerDefinition property
- 13 The headerFile property
- 14 The implements property
- 15 The includes property
- 16 The isConfigurable property
- 17 The isEnabled property
- 18 The legalValues property
- 19 The parent property
- 20 The requires property
- 21 The sourceFiles property
- 22 The valueType property
- 23 The valueFormat property
- 24 Credits
Introduction
This page contains reference information for the XCDL object definitions option, component, package and interface, followed by various properties like activeIf, sourceFiles etc.
Mandatory properties
All objects must have the following properties defined:
- id='...' - the identifier of the object, unique within the set of all configuration trees used together (computer readable, preferably using the dotted name hierarchical convention)
- name='...' - a short, human readable, descriptive name (a few words) of the object, to be used in the user interface during the configuration process (also unique)
- description='...' - a long, comprehensive description of the object, to be used as reference by the user during the configuration process.
The option object
Purpose
Define a single configuration option.
Syntax
Option( id='...', name='...', display='...', ... other properties properties ..., ... children definitions ..., )
Currently option is implemented as a Python object, constructed from class Option() with a list of properties.
Description
The option object is the basic unit of configurability. Generally each option corresponds to a single user choice. It can be a simple true/false choice, but it may also have an associated value, boolean, numeric or string (such as an array size or a device name).
The user choice is expressed during the configuration process and is reflected in the enabled/disabled statues of the option. For options with associated values, it is also possible for the user to define a value (if missing, a default value, either implicit or defined as the property defaultValue, is applied). For these options it is also possible to define some limits on the possible values that the user can choose.
A separate class of options are the computed options, whose values cannot be set by the user, but are computed according to a given expression.
Options may have associated constraints, so if this option is enabled then certain conditions have to be satisfied elsewhere in the configuration for it to be active (the activeIf property) or it may mandate for some objects to be enabled or disabled (the requires property).
If enabled and active, options can generate preprocessor #define lines in a configuration header file (if the headerDefinition property is also defined), and decide if a set of source files are included in the build process (if the sourceFiless property is also defined).
Containers
Options are not containers, so they cannot hold other options or components.
Optional properties
- activeIf=['...', ...] - a list of expressions to be evaluated if the option is enabled; if all are true, the active state of this option remains true
- category='board'/'synthetic'/'root'/'architecture'/'family' - provides additional grouping information, to allow a configuration tool to provide appropriate selections
- computed='...' - the option’s value is not directly user-modifiable, it is calculated using a suitable expression
- defaultValue='...' - an expression to be used as a default value for this option, when the user does not define it explicitly during the configuration process
- headerDefinition='...' - a valid C preprocessor identifier to be defined in the header file if the option is enabled and active
- headerFile='...' - the full path file name of the header where the option will generate a definition (if missing, inherited from the parent node)
- implements=['...', ...] - a list of ids of the interfaces implemented by this option
- isConfigurable='...' - a boolean expression that provides additional information to a configuration tool, to control if this option can be enabled/disabled by the user during the configuration process
- isEnabled='...' - a boolean expression that provides an initial value for the option enabled/disabled state
- legalValues=['...', ...] - a list of restrictions the value of this options must satisfy
- parent='...' - provides a method to break the default hierarchy and directly specify the id of the parent of this option
- requires=['...', ...] - a list of ids of other objects to be enabled when this option is enabled
- sourceFiles=['...', ...] - a list of source files that should be built if this option is enabled and active
- valueType='none'/'bool'/'int'/'float'/'string' - specify the type of the variable associated with this option
- valueFormat='...' - control how the option’s value will appear in the configuration header file
Example
TBD
The component object
Purpose
Define a component, a collection of configuration objects.
Syntax
Component( id='...', name='...', display='...', ... other properties properties ..., ... children definitions ..., )
Currently component is implemented as a Python object, constructed from class Component() with a list of properties.
Description
The component object is a configuration element that can contain additional options and sub-components. A component can have the same properties as an option. There is an additional property, includes which allows configuration data to be split into multiple files, included below the current object in the configuration hierarchy.
Similarly to options, if enabled and active, components can generate preprocessor #define lines in a configuration header file, and decide if a set of source files are included in the build process.
Components
Components are containers, so it is possible for a component to include component, option and interface objects that should go below this object in the configuration hierarchy. These objects can be defined either as embedded children, or can be read from included files.
Optional properties
- activeIf=['...', ...] - a list of expressions to be evaluated if the component is enabled; if all are true, the active state of this component remains true
- category='board'/'synthetic'/'root'/'architecture'/'family' - provides additional grouping information, to allow a configuration tool to provide appropriate selections
- children=[object, ...] - a list of children objects
- computed='...' - the component’s value is not directly user-modifiable, it is calculated using a suitable expression
- defaultValue='...' - an expression to be used as a default value for this component, when the user does not define it explicitly during the configuration process
- headerDefinition='...' - a valid C preprocessor identifier to be defined in the header file if the component is enabled and active
- headerFile='...' - the full path file name of the header where the component will generate a definition (if missing, inherited from the parent node)
- implements=['...', ...] - a list of ids of the interfaces implemented by this component
- includes=['...‘, ...] - a list of XCDL metadata files to be parsed and whose objects to be included as children of the current node
- isConfigurable='...' - a boolean expression that provides additional information to a configuration tool, to control if this component can be enabled/disabled by the user during the configuration process
- isEnabled='...' - a boolean expression that provides an initial value for the component enabled/disabled state
- legalValues=['...', ...] - a list of restrictions the value of this component must satisfy
- parent='...' - provides a method to break the default hierarchy and directly specify the id of the parent of this component
- requires=['...', ...] - a list of ids of other objects to be enabled when this component is enabled
- sourceFiles=['...', ...] - a list of source files that should be built if this component is enabled and active
- valueType='none'/'bool'/int'/'float'/'string' - specify the type of the variable associated with this component
- valueFormat='...' - control how the component's value will appear in the configuration header file
Example
TBD
The package object
Purpose
Define a package, a component that can be distributed.
Syntax
Package( id='...', name='...', display='...', ... other properties properties ..., ... children definitions ..., )
Currently package is implemented as a Python object, constructed from class Package() with a list of properties.
Description
A package is the configuration unit that users can choose whether or not to load into a specific configuration. A package can have most of the properties and behaviour of a component.
The top-level XCDL script for a package should begin with a package object.
Containers
Packages are inherently containers, so they include component, option and interface objects that should go below this object in the configuration hierarchy.
However all configuration objects that occur at the top level of the script containing the root package are automatically placed below that package in the configuration hierarchy, so putting them inside the body has the same effect (???).
Optional properties
- activeIf=['...', ...] - a list of expressions to be evaluated if the package is enabled; if all are true, the active state of this package remains true
- category='board'/'synthetic'/'root'/'architecture'/'family' - provides additional grouping information, to allow a configuration tool to provide appropriate selections
- children=[object, ...] - a list of children objects
- defaultValue='...' - an expression to be used as a default value for this package, when the user does not define it explicitly during the configuration process
- headerDefinition='...' - a valid C preprocessor identifier to be defined in the header file if the package is enabled and active
- headerFile='...' - the full path file name of the header where the package will generate a definition (if missing, inherited from the parent node)
- implements=['...', ...] - a list of ids of the interfaces implemented by this package
- includes=['...‘, ...] - a list of XCDL metadata files to be parsed and whose objects to be included as children of the current node
- isEnabled='...' - a boolean expression that provides an initial value for the package enabled/disabled state
- parent='...' - provides a method to break the default hierarchy and directly specify the id of the parent of this package
- requires=['...', ...] - a list of ids of other objects to be enabled when this package is enabled
- sourceFiles=['...', ...] - a list of source files that should be built if this package is enabled and active
- computed='...' - the package's value cannot be computed
- isConfigurable=True - packages can always be enabled/disabled by the user during the configuration process
- valueType='none' - packages do not have an associated value (might be changed to return the package version, when implemented)
- valueFormat='...' - if the header definition is generated, the value will always be (1)
- legalValues=['...', ...] - no restrictions can apply to package values
Example
Package( id="package.os.portable.infra", name="Infrastructure", description="Common types and useful macros. Tracing and assertion facilities. \ Package startup options.", headerFile="cyg/infra", headerDefinition='OS_INCLUDE_PORTABLE_INFRA', sourceFiles=[ "startup.cpp", "prestart.cpp", ], )
The interface object
Purpose
Define an interface, a functionality that can be provided by a number of different implementations.
Syntax
Interface( id='...', name='...', display='...', ... other properties properties ..., ... children definitions ..., )
Currently interface is implemented as a Python object, constructed from the class Interface() with a list of properties.
Description
An interface is a special type of computed configuration option. It provides an abstraction mechanism that can be used to simplify XCDL expressions.
As an example, suppose that some package relies on the presence of code that implements the standard kernel scheduling interface. However the requirement is no more stringent than this, so the constraint can be satisfied by the mlqueue scheduler, the bitmap scheduler, or any additional schedulers that may get implemented in the future. A first attempt at expressing the dependency might be:
requires=['isActive("package.os.portable.kernel.sched.mlqueue") or \ isActive("package.os.portable.kernel.sched.bitmap")']
This constraint is limited, it may need to be changed if a new scheduler were to be added to the system. Interfaces provide a way of expressing more general relationships:
requires=['implementations("interface.os.portable.kernel.sched.implementation") == 1']
The interface interface.os.portable.kernel.sched.implementation is implemented by both the mlqueue and bitmap schedulers, and may be implemented by future schedulers as well. The value of an interface is the number of implementors that are active and enabled, so in a typical configuration only one scheduler will be in use and the value of the interface will be 1. If all schedulers are disabled then the interface will have a value 0 and the requires constraint will not be satisfied. Some component writers may prefer to use the first requires constraint on the grounds that the code will only have been tested with the mlqueue and bitmap schedulers and cannot be guaranteed to work with any new schedulers. Other component writers may take a more optimistic view and assume that their code will work with any scheduler until proven otherwise.
Containers
Interfaces are not containers, so they cannot hold other objects such as options or components.
Optional properties
- activeIf=['...', ...] - a list of expressions to be evaluated if the interface is enabled; if all are true, the active state of this interface remains true
- category='board'/'synthetic'/'root'/'architecture'/'family' - provides additional grouping information, to allow a configuration tool to provide appropriate selections
- headerDefinition='...' - a valid C preprocessor identifier to be defined in the header file if the interface is enabled and active
- headerFile='...' - the full path file name of the header where the interface will generate a definition (if missing, inherited from the parent node)
- isEnabled='...' - a boolean expression that provides an initial value for the interface enabled/disabled state
- legalValues=['...', ...] - interfaces always have a small numerical value; the legalValues can be used to apply additional constraints such as an upper limit
- parent='...' - provides a method to break the default hierarchy and directly specify the id of the parent of this interface
- requires=['...', ...] - a list of ids of other objects to be enabled when this interface is enabled
- sourceFiles=['...', ...] - a list of source files that should be built if this interface is enabled and active
- valueFormat='...' - control how the interface's value will appear in the configuration header file
- computed='...' - the interface's value is always computed from the number of enable and active implementators
- defaultValue='...' - meaningless since the interface's value is always computed
- isConfigurable='...' - interfaces are always enabled and this cannot be changed
- valueType='int' - interfaces always have integer values
- implements=['...', ...] - interfaces cannot implemented other interfaces
Example
TBD
The activeIf property
Purpose
Allow additional control over the active state of a configuration object.
Syntax
Option( id='...', name='...', display='...', ... activeIf=['<condition>', ...], ... )
The value of the activeIf property is a list of expressions to be evaluated, usually to check hardware properties; if all these expressions are true, the active state of this object is true.
Description
Configuration options or other objects may be either active or inactive. Typically this is controlled by the object's location in the overall hierarchy. Consider the option CYGDBG_INFRA_DEBUG_PRECONDITIONS, which exists below the component CYGDBG_USE_ASSERT. If the whole component is disabled then all options it contains are inactive: there is no point in enabling preconditions unless there is generic assertion support; any requires constraints associated with preconditions are irrelevant; any sourceFiles property or other build-related property is ignored.
In some cases the hierarchy does not provide sufficient control over whether or not a particular object should be active. For example, the math library could have support for floating point exceptions which is only worthwhile if the hardware implements appropriate functionality, as specified by the architectural HAL. The relevant math library configuration options should remain below the CYGPKG_LIBM package in the overall hierarchy, but should be inactive unless there is appropriate hardware support. In cases like this an activeIf property is appropriate.
Another common use of activeIf properties is to avoid excessive nesting in the configuration hierarchy. If some option B is only relevant if option A is enabled, it is possible to turn A into a component that contains B. However adding another level to the hierarchy for a component which will contain just one entry may be considered excessive. In such cases it is possible for B to have an activeIf dependency on A.
activeIf takes a list of goal expressions as argument and all of the conditions have to be satisfied for the object to be active. For details of goal expression syntax see see the Section called Goal Expressions in Chapter 3 (!!!). In most cases the goal expressions will be very simple, often involving just one other object, but more complicated expressions can be used when appropriate.
The activeIf and requires properties have certain similarities, but they serve a different purpose. Suppose there are two options A and B, and option B relies on functionality provided by A. This could be expressed as either B activeIf A or as B requires A. The points to note are:
- If B activeIf A is used and A is disabled or inactive, then configuration (graphical) tools will generally prevent any attempt at modifying B. For example the text for B could be grayed out, and the associated checkbutton (if B is a boolean option) would be disabled. If the user needs the functionality provided by option B then it is necessary to go to option A first and manipulate it appropriately.
- If B requires A is used and A is disabled or inactive, graphical tools will still allow B to be manipulated and enabled. This would result in a new conflict which may get resolved automatically or which may need user intervention.
- If there are hardware dependencies then an activeIf condition is usually the preferred approach. There is no point in allowing the user to manipulate a configuration option if the corresponding functionality cannot possibly work on the currently selected hardware. Much the same argument applies to coarse-grained dependencies, for example if an option depends on the presence of a TCP/IP stack then an activeIf CYGPKG_NET condition is appropriate: it may be possible to satisfy the condition, but it requires the fairly drastic step of loading another package; further more, if the user wanted a TCP/IP stack in the configuration then it would probably have been loaded already.
- If option B exists to provide additional debugging information about the functionality provided by A then again an activeIf constraint is appropriate. There is no point in letting users enable extra debugging facilities for a feature that is not actually present.
- The configuration system’s inference engine will cope equally well with activeIf and requires properties. Suppose there is a conflict because some third option depends on B. If B activeIf A then the inference engine will attempt to make A active and enabled, and then to enable B if necessary. If B requires A then the inference engine will attempt to enable B and resolve the resulting conflict by causing A to be both active and enabled. Although the inference occurs in a different order, in most cases the effect will be the same.
Example
TBD
The category property
Purpose
Define additional grouping information, to allow a configuration tool to provide appropriate selections.
Syntax
Option( id='...', name='...', display='...', ... category='<string>', ... )
The value of the category property is a string.
Description
Configuration tools usually should be able to provide some choices to various selections, for example to choose from the hardware platform the build is intended for. For this to work, specific packages should be marked with additional metadata, recognised by the configuration tools.
Examples of such metadata are:
- 'board' - for hardware boards
- 'synthetic' - for synthetic platforms
Other metadata can be purely informative, like the kind of the object:
- 'root'
- 'architecture'
- 'family'
It is recommended to use this extra metadata for package objects.
Example
TBD
The children property
Purpose
Define configuration objects to be linked below the current node.
Syntax
Package( id='...', name='...', display='...', ... children=[ Object('...', ...), Object('...', ...), ], ... )
The value of the children property is a list of objects.
Description
In a typical configuration, the hierarchy of packages is automatically constructed based on the position in the file system folders. Packages usually include components, and components include options.
This hierarchical structure can be represented level by level, by listing all embedded objects in the children property.
There is no limit for the maximum depth allowed, but for readability reasons, if the hierarchy is complex, it is recommended to split it in multiple files and use the includes property.
Example
TBD
The computed property
Purpose
Used if the current option’s value is not user-modifiable, but is computed using a suitable XCDL expression.
Syntax
Option( id='...', name='...', display='...', ... computed='<expression>', ... )
The value of the computed property is an expression to be evaluated each time the object is refered.
Description
In some cases it is useful to have a configuration option whose value cannot be modified directly by the user. This can be achieved using a computed property, which takes an XCDL expression as argument (see the Section called Ordinary Expressions in Chapter 3 for a description of expression syntax). The configuration system evaluates the expression whenever needed to generate the header line or when referred from another expression. The result depends on the object’s valueType:
- valueType='none' - objects with this type have no value, so the computed property is not applicable
- valueType='bool' - the expression should evaluate to a boolean value
- valueType='number' - the expression should evaluate to a number, integer or float
- valueType='string' - the expression should evaluate to a string
There are a number of valid uses for calculated options, and there are also many cases where some other XCDL facility would be more appropriate.
Valid uses of computed objects include the following:
- On some target hardware a particular feature may be user-configurable, while on other targets it is fixed. For example some processors can operate in either big-endian or little-endian mode, while other processors do not provide any choice. It is possible to have an option CYGARC_BIGENDIAN which is calculated in some architectural HAL packages but user-modifiable in others.
- Computed objects can provide an alternative way for one package to affect the behavior of another one. Suppose a package may provide two possible implementations, a preferred one involving self-modifying code and a slower alternative. If the system involves a ROM bootstrap then the slower alternative must be used, but it would be inappropriate to modify the startup option in every HAL to impose constraints on this package. Instead it is possible to have a calculated option whose value is { CYG_HAL_STARTUP == "ROM" }, and which has appropriate consequences. Arguably this is a spurious example, and it should be a user choice whether or not to use self-modifying code with a defaultValue based on CYG_HAL_STARTUP, but that is for the component writer to decide.
- Sometimes it should be possible to perform a particular test at compile-time, for example by using a C/C++ preprocessor #if construct. However the preprocessor has only limited functionality, for example it cannot perform string comparisons. XCDL expressions are more powerful.
- Occasionally a particular sub-expression may occur multiple times in a XCDL script. If the sub-expression is sufficiently complex then it may be worthwhile to have a calculated option whose value is the sub-expression, and then reference that calculated option in the appropriate places.
Alternatives to using calculated options include the following:
- XCDL interfaces are a form of computed objects intended as an abstraction mechanism. For example, an interface can be
used to express the concept of any scheduler, as opposed to a specific one such as the bitmap scheduler.
Example
TBD
The defaultValue property
Purpose
Provide a default value for this option using an XCDL expression.
Syntax
Option( id='...', name='...', display='...', ... defaultValue='<expression>', ... )
The value of the defaultValue property is a string containing an expression to be evaluated each time the object is referred.
Description
The defaultValue property usually allows to define the initial constant value for the object.
The arguments to the defaultValue property should be an XCDL expression, see the Section called Ordinary Expressions in Chapter 3 for the syntactic details. In many cases a simple constant value will suffice.
However it is also possible for an object’s default value to depend on other objects. For example the common HAL package provides some support functions that are needed by the kernel, but are unlikely to be useful if the kernel is not being used.
If the kernel is loaded then this HAL option is automatically enabled, although the user can still disable it explicitly should this prove necessary. If the kernel is not loaded then the option is disabled, although it can still be enabled by the user if desired. defaultValue expressions can be more complicated than this if appropriate, and provide a very powerful facility for component writers who want their code to “just do the right thing” in a wide variety of configurations.
An object can have at most one defaultValue property, and it is illegal to have both a computed and a defaultValue property in one body. If an object does not have either a defaultValue or a computed property and it does not have the valueType none then the configuration tools will assume a default value expression of 0/False.
On occasion it is useful to have a configuration object A which has both a requires constraint on some other object B and a defaultValue expression of B. If option B is not enabled then A will also be disabled by default and no conflict arises. If B is enabled then A also becomes enabled and again no conflict arises. If a user attempts to enable B but not A then there will be a conflict. Users should be able to deduce that the two options are closely interlinked and should not be manipulated independently except in very unusual circumstances.
Example
TBD
The headerDefinition property
Purpose
Define the name of the header variable to be generated.
Syntax
Option( id='...', name='...', display='...', ... headerDefinition='<name>', ... )
The value of the headerDefinition property is a string containing a C/C++ preprocessor identifier.
Description
In order for a header definition to be generated, the full path of the include file where it will be added and the name of the preprocessor symbol is needed. The file name is provided by headerFile and the preprocessor symbol by headerDefinition.
The name must be a valid C/C++ preprocessor identifier: a sequence of upper or lower case letters, digits or underscores, starting with a non-digit character; identifiers beginning with an underscore should normally be avoided because they may clash with system packages or with identifiers reserved for use by the compiler.
Within a single configuration, names must be unique. If a configuration contained two packages which defined the same entity CYGIMP_SOME_OPTION, any references to that entity in a requires property or any other expression would be ambiguous. It is possible for a given name to be used by two different packages if those packages should never be loaded into a single configuration. For example, architectural HAL packages are allowed to reuse certain names because a single configuration cannot target two different architectures. For a recommended naming convention see the Section called Package Contents and Layout in Chapter 2 (?!?!).
For enabled and active objects, if the headerDefinition property is defined, a line with the following structure will be generated in the file pointed by the headerFile property:
#define <name> <value>
The format of the value is defined by the valueFormat property.
For objects with type string, a second line should be generated, with the value concatenated to the name (if the resulting string can somehow be represented as a C/C++ preprocessor identifier):
#define <name>_<value>
Example
TBD
The headerFile property
Purpose
Specify the configuration header file that will be generated for a given component, usually a package.
Syntax
Option( id='...', name='...', display='...', ... headerFile='<file name>', ... )
The value of the headerFile property is a string containing a file name.
Description
When the configuration tools generate a build tree, one of the steps is to output each objects’s configuration data to a header file. For example the kernel’s configuration data gets output to include/xcdl/kernel.h. This allows each package’s source code to #include the appropriate header file and adapt to the choices made by the user.
The name specified in a headerFile property will always be interpreted as relative to the build folder.
If missing, the definition from the parent node is used.
Example
TBD
The implements property
Purpose
Inform the framework that one or more general interfaces are implemented.
Syntax
Option( id='...', name='...', display='...', ... implements=['...', ...], ... )
The value of the implements property is a list of ids of interfaces the object implements.
Description
The XCDL interface concept provides an abstraction mechanism that can be useful in many different circumstances. Essentially an interface is a computed option whose value is the number of active and enabled objects which implement that interface. For example the interface CYGINT_KERNEL_SCHEDULER has a value corresponding to the number of schedulers in the system, typically just one.
The implements property takes a single argument, which should be a list of ids of the implemented interfaces (an object may implement multiple interfaces). These interface may be defined in the same package as the implementor or in some other package. In the latter case it may sometimes be appropriate for the implementor or the implementor’s package to have a requires property for the package containing the interface.
It is possible for an object to implement a given interface multiple times, and on occasion this is actually useful (???).
Example
TBD
The includes property
Purpose
Include additional configuration information from another XCDL script
Syntax
Option( id='...', name='...', display='...', ... includes=['...', ...], ... )
The value of the includes property is a list of file names containing other XCDL definitions.
Description
It is possible to define all the configuration options and sub-components for a given package in a single CDL script, either by nesting them in the appropriate command bodies, by extensive use of the parent property, or by some combination of these two. However for large packages this is inconvenient and it is better to split the raw configuration data over several different files. The includes property can be used to achieve this. It takes a list of filenames as argument. If the package follows the directory layout conventions then the configuration tools will look for the specified file in the meta sub-directory of the package, otherwise it will look for the file relative to the package’s top-level directory.
The includes property can only occur in the body of an component or package object.
Example
TBD
The isConfigurable property
Purpose
Define whether the current object can be manually changed by the user during the configuration process.
Syntax
Option( id='...', name='...', display='...', ... isConfigurable='<expression>', ... )
The value of the isConfigurable property is an expression evaluating to a boolean value.
Description
Most of the objects can be individually configured using the configuration (graphical) tools, but some are automatically computed and the user shouldn't be allowed to change them.
For all objects, excluding interfaces, the default is True, whether for interfaces it is False (the values associated with interfaces are computed from the number of implementations, without users intervention).
Setting isConfigurable='False' will instruct the configuration tools to no longer allow the user to enable/disable the object, and, if the object has a value, will no longer allow the user to change the value. For graphical tools, the object will be greyed.
Objects marked as non-configurable can still be active or inactive, this property does not affect the functional behaviour during builds, but only the behaviour of the configuration tools.
Example
TBD
The isEnabled property
Purpose
Define whether the current object is initially enabled or not.
Syntax
Option( id='...', name='...', display='...', ... isEnabled='<expression>', ... )
The value of the isEnabled property is an expression evaluating to a boolean value.
Description
The configuration trees are usually large, and not all objects are required in a specific configuration, so the need to individually enable/disable them.
This property controls the initial state of the object, since goal expressions can enable individual objects to satisfy requirements.
Disabling a component automatically disables all children objects.
Interfaces and packages are enabled by default, objects and components are disabled.
Objects marked as enabled can still be active or inactive.
Example
TBD
The legalValues property
Purpose
Impose constraints on the possible values for an option.
Syntax
Option( id='...', name='...', display='...', ... legalValues=['...', ...], ... )
The value of the legalValues property is a list of strings containing the restrictions.
Description
Options with the number or string valueType can have an arbitrary sequence of characters as their data. In nearly all cases some restrictions have to be imposed, for example the data should correspond to a number within a certain range, or it should be one of a small number of constants. The legalValues property can be used to impose such constraints. The arguments to the property should be an XCDL list expression, see the Section called List Expressions in Chapter 3 (?!?!) for the syntactic details. Common examples include:
legalValues=['0 to 0x7fff'] legalValues=['9600', '19200', '38400'] legalValues=["RAM", "ROM"]
The legalValues property can only be used for objects with the number or string types, since it makes little sense to further constrain a boolean object.
Example
TBD
The parent property
Purpose
Control the location of an option in the configuration hierarchy.
Syntax
Option( id='...', name='...', display='...', ... parent='...', ... )
The value of the parent property is a string containing the id of the parent object.
Description
Configuration objects live in a hierarchy of packages and components. By default a given object’s position in the hierarchy is a simple consequence of its position within the XCDL scripts, based on its position in the folder's hierarchy. Packages are generally placed at the top-level of the configuration. Any components or options that are defined at the same level as the package object in a package’s top-level XCDL script are placed immediately below that package in the hierarchy (TBI). Any options or components that are defined in the body of a package or component command, or that are read in as a result of processing a component’s includes property, will be placed immediately below that package or component in the hierarchy.
In some circumstances it is useful to specify an alternative position in the hierarchy for a given object. For example it is often convenient to re-parent device driver packages below CYGPKG_IO in the configuration hierarchy, thus reducing the number of packages at the top level of the hierarchy and making navigation easier. The parent property can be used to achieve this.
The parent property takes a single argument, which should be the id of a package or component.
Although the parent property affects an object's position in the overall hierarchy and hence whether or not that object is active, a re-parented object still belongs to the package that defines it (?!?!?!). By default any #define’s will be exported to that package’s configuration header file. Any sourceFiles properties can only reference source files present in that package, and it is not directly possible to cause some file in another package to be built by re-parenting (???).
As a special case, if an empty string is specified for the parent then the object is placed at the top of the hierarchy, ahead of any packages which are not explicitly re-parented in this way. This facility is useful for configuration options such as global preferences and default compiler flags.
Example
TBD
The requires property
Purpose
List constraints that the configuration should satisfy if a given option is active and enabled.
Syntax
Option( id='...', name='...', display='...', ... requires=['...', ...], ... )
The value of requires property is a list of strings containing expressions to be evaluated as boolean.
Description
Configuration objects are not independent. For example the C library can provide thread-safe implementations of certain functions, but only if the kernel is present, if the kernel provides multi-threading, and if the kernel options related to per-thread data are enabled. It is possible to express such constraints using requires properties.
The arguments to a requires property should constitute a list of goal expressions, as described in the Section called List Expressions in Chapter 3 (?!?!). Most goal expressions are relatively simple because the constraints being described are simple, but complicated expressions can be used when necessary. If the object is active and enabled then all these constraints should be satisfied, and any goal expressions which evaluate to 0/False will result in conflicts being raised. It is possible for users to ignore such conflicts and attempt to build the current configuration anyway, but there is no guarantee that anything will work. If an object is inactive or disabled then its requires constraints will be ignored.
The configuration system contains an inference engine which can resolve many types of conflicts automatically. For example, if option A is enabled and requires an option B that is currently disabled then the inference engine may attempt to resolve the conflict by enabling B. However this will not always be possible, for example there may be other constraints in the configuration which force B to be disabled at present, in which case user intervention is required.
Example
TBD
The sourceFiles property
Purpose
List the source files that should be built if this option is active and enabled.
Syntax
Option( id='...', name='...', display='...', ... sourceFiles=['...', ...], ... )
The value of the sourceFiles property is a list of file names containing source code to be included in the build.
Description
The sourceFiles property allows component developers to specify source files which should be compiled.
Details of the build process including such issues as compiler flags and the order in which things happen can be found in Chapter 4 (?!?!).
The sourceFiles properties can occur in any of option, component, package or interface objects. A sourceFiles property has effect if and only if the object that contains it is active and enabled. Typically the body of a package will define any source files that need to be built irrespective of individual options, and each component, option, and interface will define source files that are more specific. The sourceFiles property can list any number of source files. It is possible for a given source file to be specified in sourceFiles properties for several different objects, in which case the source file will get built if any of these objects are active and enabled.
If the package follows the directory layout conventions then the configuration tools will search for the specified source files first in the src subdirectory of the package, then relative to the package directory itself.
Note: A shortcoming of the current specification of sourceFiles properties is that there is no easy way to specify source files that should be built unless an option is enabled. It would sometimes be useful to be able to say: “if option A is enabled then compile file x.c, otherwise compile file y.c. There are two simple ways of achieving this:
- Always compile y.c, typically by listing it in the body of the package, but use #ifndef A to produce an empty object file if option A is not enabled. This has the disadvantage that the file always gets compiled and hence for some configurations builds will take longer than necessary.
- Use a computed option whose value is !A, and have a sourceFiles y.c property in its body. This has the disadvantage of adding another calculated option to the configuration.
Note: Currently it is not possible to control the priority of a sourceFiles property, in other words the order in which a file gets compiled relative to other build steps. This functionality might prove useful for complicated packages and should be added.
Example
TBD
The valueType property
Purpose
Specify the type of the value associated with a configuration object.
Syntax
Option( id='...', name='...', display='...', ... valueType='<type>', ... )
The value of the valueType property can be one of the following strings:
- 'none'
- 'bool'
- 'int'
- 'float'
- 'string'
Description
The state of an XCDL configuration option is a somewhat complicated concept. This state determines what happens when a build tree is generated: it controls what files get built and what #define’s end up in configuration header files. The state also controls the values used during expression evaluation. The key concepts are:
- An object may or may not be loaded into the current configuration. However it is still possible for packages to reference objects which are not loaded in a requires constraint or other expression. If an object is not loaded then it will have no direct effect on the build process, and 0/False will be used for expression evaluation.
- Even if an object is loaded it may still be inactive. Usually this is controlled by the object's location in the configuration hierarchy. If an object's parent is active and enabled then the option will normally be active. If the parent is either inactive or disabled then the object will be inactive. For example, if kernel timeslicing is disabled then the option CYGNUM_KERNEL_SCHED_TIMESLICE_TICKS is irrelevant and must have no effect. The activeIf property can be used to specify additional constraints. If an object is inactive then it will have no direct effect on the build process, in other words it will not cause any files to get built or #define’s to be generated. For the purposes of expression evaluation an inactive object has a value of 0/False.
- An object may be enabled or disabled. Most object are boolean in nature, for example a particular function may get inlined or it may involve a full procedure call. If an object is disabled then it has no direct effect on the build process, and for the purposes of expression evaluation it has a value of 0/False.
- An object may also have additional data associated with it, for example a numerical value used to control the size of an array, or a string used to define a greeting message.
Most objects are boolean in nature and do not have any additional associated data. For some objects only the data part makes sense and users should be unable to manipulate the enabled/disabled part of the state. For a comparatively small number of objects it makes sense to have the ability to disable that object or to enable it and associate data as well. Finally, when constructing an option hierarchy it is occasionally useful to have objects which serve only as placeholders. The valueType property can be used to control all this. There are four possible values. It should be noted that the active or inactive state of an option takes priority over the valueType: if an object is inactive then no #define’s will be generated and any build-related properties such as sourceFiles will be ignored.
- none - is intended primarily for placeholder components in the hierarchy, although it can be used for other purposes. Objects with this type do not have any additional data associated with them, so there is no way for users to modify the option. For the purposes of expression evaluation an enabled and active object with type none always has the value 1. Normal #define processing will take place, so typically a single #define will be generated using the object headerDefinition and a value of 1. Similarly build-related properties such as sourceFiles will take effect.
- bool - have a boolean data associated with them which can be edited by the user. For the purposes of expression evaluation an enabled and active object with type bool has the value True or False. Normal #define processing will take place, so typically a single #define will be generated using the object headerDefinition and a value of True/False. Similarly build-related properties such as sourceFiles will take effect.
- int - have an integer data associated with them. For the purposes of expression evaluation an enabled and active object with type int has the value of the integer number associated. Normal #define processing will take place, so typically a single #define will be generated using the object headerDefinition and an integer value. Similarly build-related properties such as sourceFiles will take effect.
- float - have a float data associated with them. For the purposes of expression evaluation an enabled and active object with type float has the value of the float number associated. Normal #define processing will take place, so typically a single #define will be generated using the object headerDefinition and a float value. Similarly build-related properties such as sourceFiles will take effect.
- string - have a string associated with them. For the purposes of expression evaluation an enabled and active object with type string has the value of the associated string. Normal #define processing will take place, and two #define lines will be generated using the object headerDefinition and the string value (if the string is empty the symbol name will be extended with _EMPTY_ instead of the value). Similarly build-related properties such as sourceFiles will take effect.
Regular objects (option, component, package) have the none type by default, but this can be changed as desired. Interfaces have the int type, and this cannot be changed, since the value of an interface is a count of the number of active and enabled interfaces.
Example
TBD
The valueFormat property
Purpose
Control how an option’s value will appear in the configuration header file.
Syntax
Option( id='...', name='...', display='...', ... valueFormat='<format>', ... )
The value of the valueFormat property is a string containing a format specification (currently in Python format).
Description
For active and enabled objects, the configuration tools will normally generate one or two #define lines in the package’s configuration header file. These take the following forms:
#define <name> <value> #define <name>_<value>
The valueFormat property can be used to control exactly what appears as the value for the first of these #define lines.
The second #define will be generated only if is a valid C/C++ preprocessor symbol results, and is not affected by the valueFormat property.
Currently the Python formatting syntax is used.
The defaults, according to valueType, are intended to surround the value by parenthesis or double quotes:
- none - '({0})'
- bool - '({0})'
- int - '({0})'
- float - '({0})'
- string - '"{0}"'
Example
TBD
Credits
The content of this page is based on Chapter 5. CDL Language Specification of The eCos Component Writer’s Guide, by Bart Veer and John Dallaway, published in 2001.