Difference between revisions of "The XCDL language"

From XCDL (eXtensible Component Definition Language) Wiki
Jump to: navigation, search
m (Language overview)
m (Language overview)
Line 2: Line 2:
  
 
== Language overview ==
 
== 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:
 
A very simple XCDL file would look like this:

Revision as of 20:47, 17 June 2014

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 commands and properties.

XCDL commands

TBD

XCDL properties

TBD

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>