The XCDL.py Definitions Reference

From XCDL (eXtensible Component Definition Language) Wiki
Jump to: navigation, search

Contents

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 hierarchical 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 requirements 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
  • requirements=['...', ...] - 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, includeFiles 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, Interface and Configuration 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
  • childrenHeaderFile='...' - the full path file name of the header where the children nodes will generate a definition (if missing, inherited from the parent node)
  • 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
  • includeFiles=['...‘, ...] - 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
  • requirements=['...', ...] - 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 with special characteristics

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, but with some limitations (see the Unavailable properties below)

The top-level XCDL script for a package should begin with a Package object.

Containers

Packages are inherently containers, so they include Component, Option, Interface, Toolchain 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
  • childrenHeaderFile='...' - the full path file name of the header where the children nodes will generate a definition (if missing, inherited from the parent node)
  • 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
  • includeFiles=['...‘, ...] - 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
  • requirements=['...', ...] - 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

Unavailable properties

  • 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 Repository object

Purpose

Define a repository, a special package.

Syntax

 Repository(
    id='...',
    name='...',
    display='...',

    ... other properties properties ...,
    ... children definitions ...,

 )

Currently Repository is implemented as a Python object, constructed from class Repository() with a list of properties.

Description

A Repository is the root node of a component repository. A Repository can have all the properties and behaviour of a Package.

Containers

Repository are inherently containers, so they include Packages, 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 repository remains true
  • buildSubFolder='...' - a subfolder below the build folder where the files related to this repository will be stored
  • category='root' - provides additional grouping information, to allow a configuration tool to provide appropriate selections
  • children=[object, ...] - a list of children objects
  • childrenHeaderFile='...' - the full path file name of the header where the children nodes will generate a definition (if missing, inherited from the parent node)
  • defaultValue='...' - an expression to be used as a default value for this repository, 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 repository is enabled and active
  • headerFile='...' - the full path file name of the header where the repository will generate a definition (if missing, inherited from the parent node)
  • implements=['...', ...] - a list of ids of the interfaces implemented by this package
  • includeFiles=['...‘, ...] - 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 repository enabled/disabled state
  • requirements=['...', ...] - 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
  • sourcesPaths=['src', '.'] - a list of paths where to search for the sources files

Unavailable properties

  • computed='...' - the repository's value cannot be computed
  • isConfigurable=True - repository can always be enabled/disabled by the user during the configuration process
  • legalValues=['...', ...] - no restrictions can apply to repository values
  • parent='...' - root nodes have no parents
  • valueType='none' - repository 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)

Example

 Repository(
    id='package.os.root',
    name='The µOS++ SE repository',
    description='All µOS++ Second Edition components are below this node.',

    # Mark this as the root of the component repository (informative).
    category='root',
    
    # The list of possible relative paths where the sources can be located, 
    # starting from package root folder.
    # This value is inherited by all children.
    sourcesPaths=['src', '.'],
    
    # Each component tree will generate a subfolder below the build folder.
    buildSubFolder='micro-os-plus',
    
    # The default dynamically generated header file, where all definitions that 
    # call for inclusions must be created.
    headerFile='include/portable/core/include/XCDL_Build_Defines.h',
    
    # The folders where to search for include files during build
    buildIncludeFolders=[
        # Each repository must contribute an include path to it's root.
        '$(REPO_DIR)', 
        
        # In addition, the folder where the dynamically generated header files
        # are stored is also used as include folder.
        '$(BUILD_DIR)/include',
    ],   
)

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 Option object. It provides an abstraction mechanism that can be used to simplify XCDL expressions. An Interface can have most of the properties and behaviour of an object, but with some limitations (see the Unavailable properties below)

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:

 requirements=['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:

 requirements=['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 requirements constraint will not be satisfied. Some component writers may prefer to use the first requirements 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
  • requirements=['...', ...] - 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

Unavailable properties

  • 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
  • implements=['...', ...] - interfaces cannot implemented other interfaces
  • isConfigurable='...' - interfaces are always enabled and this cannot be changed
  • valueType='int' - interfaces always have integer values

Example

TBD

The Toolchain object

Purpose

TBD.

Syntax

 Toolchain(
    id='...',
    name='...',
    display='...',

    ... other properties properties ...,
    ... children definitions ...,

 )

Currently Toolchain is implemented as a Python object, constructed from the class Toolchain() with a list of properties.

Description

TBD

Containers

Toolchains are containers, so they can hold other Toolchains. Toolchains are generally hierarchical, and are placed below a Package object.

Optional properties

  • compilerObjectsExtension
  • compilerDepsOptions
  • compilerOutputOptions
  • compilerInputOptions
  • compilerWarningOptions
  • compilerMiscOptions
  • compilerDebugOptions
  • compilerOptimisationOptions
  • compilerCpu
  • compilerPreprocessorOptions
  • linkerMiscOptions
  • programNamePrefix
  • programNameSuffix
  • makeObjectsVariable
  • children
  • includeFiles
  • platformSystem
  • parent
  • category

Tools

  • cc
  • cpp
  • ld
  • asm


Example

TBD

The Configuration object

Purpose

TBD.

Syntax

 Configuration(
    id='...',
    name='...',
    display='...',

    ... other properties properties ...,
    ... children definitions ...,

 )

Currently Configuration is implemented as a Python object, constructed from the class Configuration() with a list of properties.

Description

TBD

Containers

Configuration are containers, so they can hold other Configuration. Configuration are generally hierarchical, and they are placed below a Component related to the source files where the main() artefact code is located.

Optional properties

  • artifactFileName
  • buildConfigurationName
  • buildFolder
  • children
  • loadPackages
  • options
  • preprocessorSymbols
  • requirements
  • toolchain

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.

Default value

If missing, no additional constraints are considered, the object is active.

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 requirements 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 requirements 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 requirements 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 requirements 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 requirements 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 requirements 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 requirements 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.

Default value

If missing no category is assigned to the current object.

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.

Default value

If missing the current object has no direct children. However there might be indirect children, when other objects that define a parent property pointing to it.

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 includeFiles property.

Example

TBD

The childrenHeaderFile property

Purpose

Specify the configuration header file that will be generated for the children of a given component.

Syntax

 Component(
    id='...',
    name='...',
    display='...',
    ...
    childrenHeaderFile='<file name>',
    ...
 )

The value of the childrenHeaderFile property is a string containing a file name.

Default value

If missing, the definition from the parent node is used, first the childrenHeaderFile then headerFile.

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. Instead of defining a headerFile for each individual Option of a Component, it is recommended to define the file once, in the parent Component, and this definition will be used for all children nodes without a headerFile.

The name specified in a childrenHeaderFile property will always be interpreted as relative to the build folder.

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.

Default value

If missing, no computed value is used.

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.

Default value

If missing the default object value depends on the object state, active objects have the value 1/True and non active objects have the value 0/False.

Description

The defaultValue property usually allows to define the default value for the object, in case no other value is set during configuration.

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 1/True if the object is active and 0/False otherwise.

On occasion it is useful to have a configuration object A which has both a requirements 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.

Default value

If missing, no header definition is generated for this object.

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 requirements 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.

Default value

If missing, the definition from the parent node is used, first the childrenHeaderFile, then the headerFile.

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.

If multiple nodes use the same file, it is recommended to define the file only once, in the parent node, using childrenHeaderFile.

The name specified in a headerFile property will always be interpreted as relative to the build folder.

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.

Default value

If missing it is assumed that the current object does not implement any interface.

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 requirements property for the package containing the interface.

It is possible for an object to implement a given interface multiple times, but only distinct implementor ids are counted.

Example

TBD

The includeFiles property

Purpose

Include additional configuration information from another XCDL script

Syntax

 Option(
    id='...',
    name='...',
    display='...',
    ...
    includeFiles=['...', ...],
    ...
 )

The value of the includeFiles property is a list of file names containing other XCDL definitions.

Default value

If missing, no additional XCDL files are parsed as children of the current object.

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 includeFiles 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 includeFiles 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=True|False|'<expression>',
    ...
 )

The value of the isConfigurable property is the True/False constant or an expression evaluating to a boolean value.

Default value

If missing, the default is True, all objects are configurable.

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.

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=True|False|'<expression>',
    ...
 )

The value of the isEnabled property is the True/False constant or an expression evaluating to a boolean value.

If the value is constant, it is evaluated when parsing the file, if the value is a string, it is evaluated in a separate step that process the entire configurations trees, before processing the requirements.

Default value

If missing, the default is True, all objects are enabled.

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. Objects marked as enabled can still be active or inactive.

Disabling a component or package automatically disables all children objects.

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.

Default value

If missing, no value based constraints are imposed.

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.

Default value

If missing (the usual case), the surrounding object is considered the natural parent of this object. If none, the package object in the parent folder is used.

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 includeFiles 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 requirements property

Purpose

List constraints that the configuration should enforce/satisfy if a given option is active and enabled.

Syntax

 Option(
    id='...',
    name='...',
    display='...',
    ...
    requirements=['...', ...],
    ...
 )

The value of requirements property is a list of strings containing expressions to be evaluated as boolean.

Default value

If missing, no additional requirements will be enforced/checked.

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 requirements properties.

The arguments to a requirements 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 requirements 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 requirements 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.

Accepted functions:

  • enable()
  • disable()
  • setValue()

(For convenience, in configuration nodes, setValue() also enables the node and all parent nodes).

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.

Default value

If missing, no source files will be added tot he 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'

Default value

If missing, the default is none, except for interfaces, where the type is always int and cannot be changed.

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 requirements 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).

Default value

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}"'

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.

Example

To format a 32bit hex value, use:

 valueFormat='(0x{0:08X})',

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.