The XCDL language

From XCDL (eXtensible Component Definition Language) Wiki
Revision as of 23:12, 17 June 2014 by Ilg (Talk | contribs)

Jump to: navigation, search

The XCDL definitions are a key part of the XCDL component framework. All packages must come with at least one XCDL file, to describe that package to the framework. The information in that file includes details of all the configuration options and how to build the package. Implementing a new component or turning some existing code into an XCDL component always involves writing corresponding XCDL. This chapter provides a description of the XCDL language. Detailed information on specific parts of the language can be found in The XCDL Language specification.

Language overview

XCDL is not exactly a new language, it is more like a direct representation in XML of the original eCos CDL definitions.

A very simple XCDL file would look like this:

<package name="CYGPKG_ERROR">

  <display>Common error code support</display>
  <description>This package contains the common list of error and
  status codes. It is held centrally to allow
  packages to interchange error codes and status
  codes in a common way, rather than each package
  having its own conventions for error/status
  reporting. The error codes are modelled on the
  POSIX style naming e.g. EINVAL etc. This package
  also provides the standard strerror() function to
  convert error codes to textual representation.</description>

  <compile>strerror.cxx</compile>
  <include_dir>cyg/error</include_dir>

</package>

This describes a single package, the error code package, which does not have any sub-components or configuration options. The package has an internal name, CYGPKG_ERROR, which can be referenced in other XCDL scripts using e.g. requires CYGPKG_ERROR. There will also be a #define for this symbol in a configuration header file. In addition to the package name, this script provides a number of properties for the package as a whole. The display property provides a short description. The description property involves a rather longer one, for when users need a bit more information. The compile and include_dir properties list the consequences of this package at build-time. The package appears to lack any on-line documentation.

Packages could be even simpler than this. If the package only provides an interface and there are no files to be compiled then there is no need for a compile property. Alternatively if there are no exported header files, or if the exported header files should go to the top-level of the install/include directory, then there is no need for an include_dir property. Strictly speaking the description and display properties are optional as well, although application developers would not appreciate the resulting lack of information about what the package is supposed to do.

However many packages tend to be a bit more complicated than the error package, containing various subcomponents and configuration options. These are also defined in the XCDL files and in much the same way as the package. For example, the following excerpt comes from the infrastructure package:

<component name="CYGDBG_INFRA_DEBUG_TRACE_ASSERT_BUFFER">

  <display>Buffered tracing</display>
  <description>An output module which buffers output from tracing and
  assertion events. The stored messages are output when an
  assert fires, or CYG_TRACE_PRINT() (defined in
  <cyg/infra/cyg_trac.h>) is called. Of course, there will
  only be stored messages if tracing per se (CYGDBG_USE_TRACING) is enabled above.</description>

  <default_value>1</default_value>
  <active_if>CYGDBG_USE_TRACING</active_if>

  <option name="CYGDBG_INFRA_DEBUG_TRACE_BUFFER_SIZE">

    <display>Trace buffer size</display>
    <flavor>data</flavor>
    <default_value>32</default_value>
    <legal_values>5 to 65535</legal_values>
    <description>The size of the trace buffer. This counts the number of
    trace records stored. When the buffer fills it either
    wraps, stops recording, or generates output.</description>
  </option>

  <!-- ... -->

</package>

Like a <package>, a <component> has a name and a body. The body contains various properties for that component, and may also contain subcomponents or options. Similarly a <option> has a name and a body of properties. This example lists a number of new properties: <default_value>, <active_if>, <flavor> and <legal_values>. The meaning of most of these should be fairly obvious. The next sections describe the various XCDL objects and properties.

XCDL objects

There are four XCDL-related objects which can occur at the top-level of a XCDL script: <package>, <component>, <option> and <interface>. These correspond to the basic building blocks of the language. All of these take the same basic form:

<package name="...">
  ...
</package>

<component name="..."> 
  ...
</component>

<option name="...">
   ...
</option>

<interface name="..."> 
  ...
</interface>

All objects have a mandatory name and, inside their body, a list of properties. Packages and components can contain other entities, so the <package> and <component> can also have nested objects in their bodies. All names must be unique within a given configuration. If say the C library package and a TCP/IP stack both defined an option with the same name then it would not be possible to load both of them into a single configuration. There is a naming convention which should make accidental name clashes very unlikely.

It is possible for two packages to use the same name if there are no reasonable circumstances under which both packages could be loaded at the same time. One example would be architectural HAL packages: a given XCDL configuration can be used on only one processor, so the architectural HAL packages CYGPKG_HAL_ARM and CYGPKG_HAL_I386 can re-use option names; in fact in some cases they are expected to.

Each package has one top-level XCDL script. Typically the name of this top-level script is related to the package, so the kernel package uses kernel.cdl, but this is just a convention. The first object in the top-level file should be <package>. There should be only one <package> object per package.

The various XCDL entities live in a hierarchy. For example the kernel package contains a scheduling component, a synchronization primitives component, and a number of others. The synchronization component contains various options such as whether or not mutex priority inheritance is enabled. There is no upper bound on how far components can be nested, but it is rarely necessary to go more than three or four levels deeper than the package level. Since the naming convention incorporates bits of the hierarchy, this has the added advantage of keeping the names down to a more manageable size.

The hierarchy serves two purposes. It allows options to be controlled en masse, so disabling a component automatically disables all the options below it in the hierarchy. It also permits a much simpler representation of the configuration in the graphical configuration tool, facilitating navigation and modification.

By default a package is placed at the top-level of the hierarchy, but it is possible to override this using a parent property. For example an architectural HAL package such as CYGPKG_HAL_SH typically re-parents itself below CYGPKG_HAL, and a platform HAL package would then re-parent itself below the architectural HAL. This makes it a little bit easier for users to navigate around the hierarchy. Components, options and interfaces can also be re-parented, but this is less common.

As required by the XML specs, there can be only one object at the top of the hierarchy in a XCDL file.

Components can also contain options and other XCDL entities, in fact that is what distinguishes them from options. These can be defined in the body of the <component>:

<component name="CYGPKG_LIBC_STDIO">

  <component name="CYGPKG_LIBC_STDIO_FLOATING_POINT">
    ...
  </component>

  <option name="CYGSEM_LIBC_STDIO_THREAD_SAFE_STREAMS">
    ...
  </option>

</component>

Nesting options inside the bodies of components like this is fine for simple packages with only a limited number of configuration options, but it becomes unsatisfactory as the number of options increases. Instead it is possible to split the XCDL data into multiple XCDL scripts, on a per-component basis. The script property should be used for this. For example, in the case of the C library all stdio-related configuration options could be put into stdio.cdl, and the top-level XCDL script libc.cdl would contain the following:

<package name="CYGPKG_LIBC">
  ...
  <component name="CYGPKG_LIBC_STDIO">
    ...
    <script>stdio.xml</script>
  </component>

</package>

The CYGPKG_LIBC_STDIO_FLOATING_POINT component and the CYGSEM_LIBC_STDIO_THREAD_SAFE_STREAMS option can then be placed at the top-level of stdio.xml. It is possible to have some options nested in the body of a <component> object and other options in a separate file accessed by the script property. In such a case the nested options would be processed first, and then the other script would be read in. A script specified by a script property should only define new options, components or interfaces: it should not contain any additional properties for the current component.

It is possible for a component’s XCDL script to have a sub-component which also has a script property, and so on. In practice excessive nesting like this is rarely useful. It is also possible to ignore the XCDL language support for constructing hierarchies automatically and use the parent property explicitly for every single option and component. Again this is not generally useful.

XCDL properties

Each package, component, option, and interface has a list of properties, which provide the component framework with information about how to handle each option. For example there is a property for a descriptive text message which can be displayed to a user who is trying to figure out just what effect manipulating the option would have on the target application. There is another property for the default value, for example whether a particular option should be enabled or disabled by default.

All of the properties are optional, it is legal to define a configuration option which has an empty body. However some properties are more optional than others: users will not appreciate having to manipulate an option if they are not given any sort of description or documentation. Other properties are intended only for very specific purposes, for example make_object and include_files, and are used only rarely.

Because different properties serve very different purposes, their syntax is not as uniform as the top-level objects. Some properties take no arguments at all. Other properties take a single argument such as a description string, or a list of arguments such as a compile property which specifies the file or files that should be compiled if a given option is active and enabled. The define_proc property takes as argument a snippet of Tcl code. The active_if, calculated, default_value, legal_values and requires properties take various expressions. Additional properties may be defined in future which take new kinds of arguments. (TODO: clarify this for XML)

Many of the properties can be used in any of <package>, <component>, <option> or <interface>. Other properties are more specific. The script property is only relevant to components. The define_header, hardware, include_dir, include_files, and library properties apply to a package as a whole, so can only occur in the body of a <package> object. The calculated, default_value, legal_values and flavor properties are not relevant to packages, as will be explained later. The calculated and default_value properties are also not relevant to interfaces.

This section lists the various properties, grouped by purpose. Each property also has a full reference page in The XCDL language specification. Properties related to values and expressions are described in more detail in the Section called Values and Expressions. Properties related to header file generation and to the build process are described in The build process.

Information-providing properties

Users can only be expected to manipulate configuration options sensibly if they are given sufficient information about these options. There are three properties which serve to explain an option in plain text: the display property gives a textual alias for an option, which is usually more comprehensible than something like CYGPKG_LIBC_TIME_ZONES; the description property gives a longer description, typically a paragraph or so; the doc property specifies the location of additional on-line documentation related to a configuration option. In the context of a graphical tool the display string will be the primary way for users to identify configuration options; the description paragraph will be visible whenever the option is selected; the on-line documentation will only be accessed when the user explicitly requests it.

<package name="CYGPKG_UITRON">

  <display>uITRON compatibility layer</display>
  <doc>ref/ecos-ref.a.html</doc>
  <description>eCos supports a uITRON Compatibility Layer, providing
  full Level S (Standard) compliance with Version 3.02 of
  the uITRON Standard, plus many Level E (Extended) features.
  uITRON is the premier Japanese embedded RTOS standard.</description>
</package>

All three properties take a single argument. For display and description this argument is just a string. For doc it should be a pointer to a suitable HTML file, optionally including an anchor within that page. If the directory layout conventions are observed then the component framework will look for the HTML file in the package’s doc sub-directory, otherwise the doc filename will be treated as relative to the package’s top-level directory.

The configuration hierarchy

There are two properties related to the hierarchical organization of components and options: <parent> and <script>.

The parent property can be used to move a XCDL entity somewhere else in the hierarchy. The most common use is for packages, to avoid having all the packages appear at the top-level of the configuration hierarchy. For example an architectural HAL package such as CYGPKG_HAL_SH is placed below the common HAL package CYGPKG_HAL using a parent property.

<package name="CYGPKG_HAL_SH">
  <display>SH architecture</display>
  <parent>CYGPKG_HAL</parent>
  ...
</package>

The parent property can also be used in the body of a <component>, <option> or <interface>, but this is less common. However care has to be taken since excessive re-parenting can be confusing. Care also has to be taken when reparenting below some other package that may not actually be loaded in a given configuration, since the resulting behavior is undefined.

As a special case, if the parent is the empty string then the XCDL entity is placed at the root of the hierarchy. This is useful for global preferences, default compiler flags, and other settings that may affect every package. (TODO: decide if we keep this)

The script property can only be used in the body of a <component> object. The property takes a single filename as argument, and this should be another XCDL file containing additional options, sub-components and interfaces that should go below the current component in the hierarchy. If the directory layout conventions are observed then the component framework will look for the specified file relative to the meta subdirectory of the package, otherwise the filename will be treated as relative to the package’s top-level directory.

<component name="CYGPKG_LIBC_STDIO">
  <display>Standard input/output functions</display>
  <flavor>bool</flavor>
  <requires>CYGPKG_IO</requires>
  <requires>CYGPKG_IO_SERIAL_HALDIAG</requires>
  <default_value>1</default value>
  <description>This enables support for standard I/O functions from <stdio.h>.</description>
  <script>stdio.xml</script>
</component>

Value-related properties

There are seven properties which are related to option values and state: flavor, calculated, default_value, le- gal_values, active_if, implements, and requires. More detailed information can be found in the Section called Values and Expressions.

In the context of configurability, the concept of an option’s value is somewhat non-trivial. First an option may or may not be loaded: it is possible to build a configuration which has the math library but not the kernel; however the math library’s XCDL scripts still reference kernel options, for example CYGSEM_LIBM_THREAD_SAFE_COMPAT_MODE has a requires constraint on CYGVAR_KERNEL_THREADS_DATA. Even if an option is loaded it may or may not be active, depending on what is happening higher up in the hierarchy: if the C library’s CYGPKG_LIBC_STDIO component is disabled then some other options such as CYGNUM_LIBC_STDIO_BUFSIZE become irrelevant. In addition each option has both a boolean enabled/disabled flag and a data part. For many options only the boolean flag is of interest, while for others only the data part is of interest. The flavor property can be used to control this:

  • flavor none
This flavor indicates that neither the boolean nor the data parts are user-modifiable: the option is always enabled and the data is always set to 1. The most common use for this is to have a component that just acts as a placeholder in the hierarchy, allowing various options to be grouped below it.
  • flavor bool
Only the boolean part of the option is user-modifiable. The data part is fixed at 1.
  • flavor data
Only the data part of the option is user-modifiable. The boolean part is fixed at enabled.
  • flavor booldata
Both the boolean and the data part of the option are user-modifiable.

For more details of XCDL flavors and how a flavor affects expression evaluation, and other consequences, see the Section called Values and Expressions. The flavor property cannot be used for a package because packages always have the booldata flavor. Options and components have the bool flavor by default, since most configuration choices are simple yes-or-no choices. Interfaces have the data flavor by default.

The calculated property can be used for options which should not be user-modifiable, but which instead are fixed by the target hardware or determined from the current values of other options. In general calculated options should be avoided, since they can be confusing to users who need to figure out whether or not a particular option can actually be changed. There are a number of valid uses for calculated options, and quite a few invalid ones as well. The reference packages should be consulted for further details. The property takes an ordinary XCDL expression as argument, for example:

<!-- A constant on some target hardware, perhaps user-modifiable on other targets. -->
<option name="CYGNUM_HAL_RTC_PERIOD">
  <display>Real-time clock period</display>
  <flavor>data</data>
  <calculated>12500</calculated>
</option>

The calculated property cannot be used for packages or interfaces. The value of a package always corresponds to the version of that package which is loaded, and this is under user control. Interfaces are implicitly calculated, based on the number of active and enabled implementors.

The default_value property is similar to calculated, but only specifies a default value which users can modify. Again this property is not relevant to packages or interfaces. A typical example would be:

<option name="CYGDBG_HAL_DEBUG_GDB_THREAD_SUPPORT">
  <display>Include GDB multi-threading debug support</display>
  <requires>CYGDBG_KERNEL_DEBUG_GDB_THREAD_SUPPORT</requires>
  <default_value>CYGDBG_KERNEL_DEBUG_GDB_THREAD_SUPPORT</requires>
  ...
</option>

The legal_values property imposes a constraint on the possible values of the data part of an option. Hence it is only applicable to options with the data or booldata flavors. It cannot be used for a package since the only valid value for a package is its version number. The arguments to the legal_values property should constitute a XCDL list expression.

<option name="CYGNUM_LIBC_TIME_STD_DEFAULT_OFFSET">
  <display>Default Standard Time offset</display>
  <flavor>data</data>
  <legal_values>-90000 to 90000</legal_values>
  <default_value>0</default_value>
  ...
</option>

The active_if property does not relate directly to an option’s value, but rather to its active state. Usually this is controlled via the configuration hierarchy: if the CYGPKG_LIBC_STDIO component is disabled then all options below it are inactive and do not have any consequences. In some cases the hierarchy does not provide sufficient control, for example an option should only be active if two disjoint sets of conditions are satisfied: the hierarchy could be used for one of these conditions, and an additional active_if property could be used for the other one. The arguments to active_if should constitute a XCDL goal expression.

<!--  Do not provide extra semaphore debugging if there are no semaphores -->
<option name="CYGDBG_KERNEL_INSTRUMENT_BINSEM">
  <active_if>CYGPKG_KERNEL_SYNCH</active_if>
  ...
</option>

The implements property is related to the concept of XCDL interfaces. If an option is active and enabled and it implements a particular interface then it contributes 1 to that interface’s value.

<package name="CYGPKG_NET_EDB7XXX_ETH_DRIVERS">
  <display>Cirrus Logic ethernet driver</display>
  <implements>CYGHWR_NET_DRIVERS</implements>
  <implements>CYGHWR_NET_DRIVER_ETH0</implements>
  ...
</package>

The requires property is used to impose constraints on the user’s choices. For example it is unreasonable to expect the C library to provide thread-safe implementations of certain functions if the underlying kernel support has been disabled, or even if the kernel is not being used at all.

<option name="CYGSEM_LIBC_PER_THREAD_ERRNO>
  <display>Per-thread errno</display>
  <doc>ref/ecos-ref.15.html</doc>
  <requires>CYGVAR_KERNEL_THREADS_DATA</requires>
  <default_value>1</default_value>
  ...
</option>

The arguments to the requires property should be a XCDL goal expression.

Generating the Configuration Header Files

When creating or updating a build tree the component framework will also generate configuration header files, one per package. By default it will generate a #define for each option, component or interface that is active and enabled. For options with the data or booldata flavors the #define will use the option’s data part, otherwise it will use the constant 1. Typical output would include:

#define CYGFUN_LIBC_TIME_POSIX 1
#define CYGNUM_LIBC_TIME_DST_DEFAULT_STATE -1

There are six properties which can be used to control the header file generation process: define_header, no_define, define_format, define, if_define, and define_proc.

By default the component framework will generate a configuration header file for each package based on the package’s name: everything up to and including the first underscore is discarded, the rest of the name is lower-cased, and a .h suffix is appended. For example the configuration header file for the kernel package CYGPKG_KERNEL is pkgconf/kernel.h. The define_header property can be used to specify an alternative file- name. This applies to all the components and options within a package, so it can only be used in the body of a <package> object. For example the following specifies that the configuration header file for the SPARClite HAL package is pkgconf/hal_sparclite.h.

<package name="CYGPKG_HAL_SPARCLITE">
  <display>SPARClite architecture</display>
  <parent>CYGPKG_HAL</parent>
  <hardware/>
  <define_header>hal_sparclite.h</define_header>
  ...
</package>

The no_define property is used to suppress the generation of the default #define. This can be useful if an option’s consequences are all related to the build process or to constraints, and the option is never actually checked in any source code. It can also be useful in conjunction with the define, if_define or define_proc properties. The no_define property does not take any arguments.

<component name=CYG_HAL_STARTUP">
  <display>Startup type</display>
  <flavor>data</flavor>
  <legal_values>{ "RAM" "ROM" }</legal_values>
  <default_value>{"RAM"}</default_value>
  <no_define/>
  <define>-file system.h CYG_HAL_STARTUP</define>
  ...
</component>

This example also illustrates the define property, which can be used to generate a #define in addition to the default one. It takes a single argument, the name of the symbol to be defined. It also takes options to control the configuration header file in which the symbol should be defined and the format to be used.

The define_format property can be used to control how the value part of the default #define gets formatted. For example a format string of "0x%04x" could be used to generate a four-digit hexadecimal number.

The if_define property is intended for use primarily to control assertions, tracing, and similar functionality. It supports a specific implementation model for these, allowing control at the grain of packages or even individual source files. The reference pages provide additional information.

The define_proc property provides an escape mechanism for those cases where something special has to happen at configuration header file generation time. It takes a single argument, a fragment of Tcl code, which gets executed when the header file is generated. This code can output arbitrary data to the header file, or perform any other actions that might be appropriate. (TODO: no Tcl code in XCDL...)

Controlling what gets Built

There are six properties which affect the build process: compile, make, make_object, library, include_dir, and include_files. The last three apply to a package as a whole, and can only occur in the body of a <package> object.

Most of the source files that go into a package should simply be compiled with the appropriate compiler, selected by the target architecture, and with the appropriate flags, with an additional set defined by the target hardware and possible modifications on a per-package basis. The resulting object files will go into the library libtarget.a, which can then be linked against application code. The compile property is used to list these source files:

<package name="CYGPKG_ERROR">
  <display>Common error code support</display>
  <compile>strerror.cxx</compile>
  <include_dir>cyg/error</include_dir>
  ...
</package>

The arguments to the compile property should be one or more source files. Typically most of the sources will be needed for the package as a whole, and hence they will be listed in one or more compile properties in the body of the <package>. Some sources may be specific to particular configuration options, in other words there is no point in compiling them unless that option is enabled, in which case the sources should be listed in a compile property in the corresponding <option>, <component> or <interface> body.

Some packages may have more complicated build requirements, for example they may involve a special target such as a linker script which should not end up in the usual library, or they may involve special build steps for generating an object file. The make and make_object properties provide support for such requirements, for example:

<package name="CYGPKG_HAL_MN10300_AM33">
  <display>MN10300 AM33 variant</display>
  ...
  <make>
    <PREFIX>/lib/target.ld: <PACKAGE>/src/mn10300_am33.ld
    $(CC) -E -P -Wp,-MD,target.tmp -DEXTRAS=1 -xc $(INCLUDE_PATH) \
    $(CFLAGS) -o $@ $<
    @echo $@ ": \\" > $(notdir $@).deps @tail +2 target.tmp >> $(notdir $@).deps @echo >> $(notdir $@).deps
    @rm target.tmp</make>
</package>

For full details of custom build steps and the build process generally, see The build process.

By default, in eCos, all object files go into the library libtarget.a. It is possible to override this at the package level using the library property, but this should be avoided since it complicates application development: instead of just linking with a single library for all eCos-related packages, it suddenly becomes necessary to link with several libraries.

The include_dir and include_files properties relate to a package’s exported header files. By default a package’s header files will be exported to the install/include directory. This is the desired behavior for some packages like the C library, since headers like stdio.h should exist at that level. However if all header files were to end up in that directory then there would be a significant risk of a name clash. Instead it is better for packages to specify some sub-directory for their exported header files, for example:

<package name="CYGPKG_INFRA">
  <display>Infrastructure</display>
  <include_dir>cyg/infra</include_dir>
  ...
</package>

The various header files exported by the infrastructure, for example cyg_ass.h and cyg_trac.h will now end up in the install/include/cyg/infra sub-directory, where a name clash is very unlikely.

For packages which follow the directory layout conventions the component framework will assume that the package’s include sub-directory contains all exported header files. If this is not the case, for example because the package is sufficiently simple that the layout convention is inappropriate, then the exported header files can be listed explicitly in an include_files property.

Miscellaneous Properties

The hardware property is only relevant to packages. Some packages such as device drivers and HAL packages are hardware-specific, and generally it makes no sense to add such packages to a configuration unless the corresponding hardware is present on your target system. Typically hardware package selection happens automatically when you select your target. The hardware property should be used to identify a hardware-specific package, and does not take any arguments.

<package name="CYGPKG_HAL_MIPS">
  <display>MIPS architecture</display>
  <parent>CYGPKG_HAL</parent>
  <hardware/>
  <include_dir>cyg/hal</include_dir>
  <define_header>hal_mips.h</define_header>
  ...
</package>

Option naming convention

TBD

Values and expressions

It is fairly reasonable to expect that enabling or disabling a configuration option such as CYGVAR_KERNEL_THREADS_DATA in some way affects its value. This will have an effect on any expressions that reference this option such as requires CYGVAR_KERNEL_THREADS_DATA. It will also affect the consequences of that option: how it affects the build process and what happens to any constraints that CYGVAR_KERNEL_THREADS_DATA may impose (as opposed to constraints on this option imposed by others).

In a language like C the handling of variables is relatively straightforward. If a variable x gets referenced in an expression such as if (x != 0), and that variable is not defined anywhere, then the code will fail to build, typically with an unresolved error at link-time. Also in C a variable x does not live in any hierarchy, so its value for the purposes of expression evaluation is not affected by anything else. C variables also have a clear type such as int or long double.

In XCDL things are not so straightforward.

Option values

There are four factors which go into an option’s value:

  1. An option is part of a package that may or may not be loaded
  2. If the parent package is loaded, the option may or may not be active
  3. Even if the option is active, it may or may not be enabled
  4. If the option is in a loaded package, is active and enabled, then it will have some associated data which constitutes its value.

Is the option loaded?

At any one time a configuration will contain only a subset of all possible packages. In fact it is impossible to combine certain packages in a single configuration. For example architectural HAL packages should contain a set of options defining endianness, the sizes of basic data types and so on (many of which will of course be constant for any given architecture). Any attempt to load two architectural HAL packages into a configuration will fail because of the resulting name clash. Since XCDL expressions can reference options in other packages, and often need to do so, it is essential to define the resulting behaviour.

One complication is that the component framework does not know about every single option in every single package. Obviously it cannot know about packages from arbitrary third parties which have not been installed. If a XCDL expression contains a reference to some option CYGSEM_KERNEL_SCHED_TIMESLICE then the component framework will only know about this option if the kernel package is actually loaded into the current configuration.

Any options which are not in the current configuration are handled as follows:

  1. Any references to that option will evaluate to 0/false, so requires !CYGSEM_KERNEL_SCHED_TIMESLICE will be satisfied but requires CYGSEM_KERNEL_THREADS_DATA will not be satisfied.
  2. An option that is not loaded has no consequences on the build process. It cannot directly result in any #define’s in a configuration header file, nor in any files being compiled. This is only reasonable: if the option is not loaded then the component framework has no way of knowing about any compile or similar properties. An option that is not loaded can have indirect consequences by being referenced in XCDL expressions.
  3. An option that is not loaded cannot impose any constraints on the rest of the configuration. Again this is the only reasonable behavior: if the option is not loaded then any associated requires or legal_values properties will not be known.

Is the option active?

The next issue to consider is whether or not a particular option is active. Configuration options are organized in a hierarchy of components and sub-components. For example the C library package contains a component CYGPKG_LIBC_STDIO containing all the options related to standard I/O. If a user disables the component as a whole then all the options below it become inactive: it makes no sense to disable all stdio functionality and then manipulate the buffer sizes.

Inactive is not quite the same as disabled, although the effects are similar. The value of an inactive option is preserved. If the user modifies a buffer size option, then disables the whole stdio component, the buffer size value remains in case the stdio component is re-enabled later on. Some tools such as the graphical configuration tool will treat inactive options specially, for example such options may be grayed out.

The active or inactive state of an option may affect other packages. For example a package may use the sprintf function and require support for floating point conversions, a constraint that is not satisfied if the relevant option is inactive. It is necessary to define exactly what it means for an option to be inactive:

  1. An option is inactive if its parent is either inactive or disabled. For example if CYGPKG_LIBC_STDIO is disabled then all the options and sub-components become inactive; since CYGPKG_LIBC_STDIO_FLOATING_POINT is now inactive, CYGSEM_LIBC_STDIO_PRINTF_FLOATING_POINT is inactive as well.
  2. Options may also be inactive as a result of an active_if property. This is useful if a particular option is only relevant if two or more disjoint sets of conditions need to be satisfied, since the hierarchical structure can only cope with at most one such set.
  3. If an option is inactive then any references to that option in XCDL expressions will evaluate to 0/false. Hence a constraint of the form requires CYGSEM_LIBC_STDIO_PRINTF_FLOATING_POINT is not satisfied if the entire stdio component is disabled.
  4. An option that is inactive has no consequences on the build process. No #define will be generated. Any compile or similar properties will be ignored.
  5. An option that is inactive cannot impose any constraints on the rest of the configuration. For example CYGSEM_LIBC_STDIO_PRINTF_FLOATING_POINT has a dependency requires CYGPKG_LIBM, but if all of the stdio functionality is disabled then this constraint is ignored (although of course there may be other packages which have a dependency on CYGPKG_LIBM).

Is the option enabled? What is the data?

The majority of configuration options are boolean in nature, so the user can either enable or disable some functionality. Some options are different. For example CYGNUM_LIBC_STDIO_BUFSIZE is a number, and CYGDAT_LIBC_STDIO_DEFAULT_CONSOLE is a string corresponding to a device name. A few options like CYGDAT_UITRON_TASK_EXTERNS can get very complicated. XCDL has to cope with this variety, and define the exact behavior of the system in terms of constraints and build-time consequences.

In XCDL the value of an option consists of two parts. There is a boolean part, controlling whether or not the option is enabled. There is also a data part, providing additional information. For most options one of these parts is fixed, as controlled by the option’s flavor property:

Flavor Enabled Data
none Always enabled 1, not modifiable
bool User-modifiable 1, not modifiable
data Always enabled User-modifiable
booldata User-modifiable User-modifiable

The effects of the boolean and data parts are as follows:

  1. If an option is disabled, in other words if the boolean part is false, then any references to that option in XCDL expressions will evaluate to 0. This is the same behavior as for inactive options. The data part is not relevant. The none and data flavors specify that the option is always enabled, in which case this rule is not applicable.
  2. If an option is enabled then any references to that option in XCDL expressions will evaluate to the option’s data part. For two of the flavors, none and bool, this data part is fixed to the constant 1 which generally has the expected result.
  3. If a component or package is disabled then all sub-components and options immediately below it in the hierarchy are inactive. By a process of recursion this will affect all the nodes in the subtree.
  4. If an option is disabled then it can impose no constraints on the rest of the configuration, in particular requires and legal_values properties will be ignored. If an option is enabled then its constraints should be satisfied, or the component framework will report various conflicts. Note that the legal_values constraint only applies to the data part of the option’s value, so it is only useful with the data and booldata flavors. Options with the none and data flavors are always enabled so their constraints always have to be satisfied (assuming the option is active).
  5. If an option is disabled then it has no direct consequences at build-time: no #define will be generated, no files will get compiled, and so on. If an option is active and enabled then all the consequences take effect. The option name and data part are used to generate the #define in the appropriate configuration header file, subject to various properties such as no_define, but the data part has no other effects on the build system.

By default all options and components have the bool flavor: most options are boolean in nature, so making this the default allows for slightly more compact XCDL files. Packages have the booldata flavor, where the data part always corresponds to the version of the package that is loaded into the configuration: changing this value corresponds to unloading the old version and loading in a different one.

Some examples

The following definitions, can be used to illustrate how values and flavors work in practice:

<component name="CYGPKG_LIBC_RAND">

    <flavor>none</flavor>
    <compile>stdlib/rand.cxx</compile>

    <option name="CYGSEM_LIBC_PER_THREAD_RAND">
        <requires>CYGVAR_KERNEL_THREADS_DATA</requires>
        <default_value>0</default_value>
    </option>

    <option name="CYGNUM_LIBC_RAND_SEED">
        <flavor>data</flavor>
        <legal_values>0 to 0x7fffffff</legal_values>
        <default_value>1</default_value>
    </option>

    <option name="CYGNUM_LIBC_RAND_TRACE_LEVEL">
        <flavor>data</flavor>
        <legal_values>0 to 1</legal_values>
        <default_value>0</default_value>
    </option>
</component>