Chapter 3. The CDL Language
Table of Contents
The CDL language is a key part of the eCos component framework. All packages must come with at least one CDL script, to describe that package to the framework. The information in that script includes details of all the configuration options and how to build the package. Implementing a new component or turning some existing code into an eCos component always involves writing corresponding CDL. This chapter provides a description of the CDL language. Detailed information on specific parts of the language can be found in Chapter 5, CDL Language Specification.
3.1. Language Overview
A very simple CDL script would look like this:
cdl_package CYGPKG_ERROR { display "Common error code support" compile strerror.cxx include_dir cyg/error 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." }
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 CDL 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 sub-components and configuration options. These are also defined in the CDL scripts and in much the same way as the package. For example, the following excerpt comes from the infrastructure package:
cdl_component CYGDBG_INFRA_DEBUG_TRACE_ASSERT_BUFFER { display "Buffered tracing" default_value 1 active_if CYGDBG_USE_TRACING 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." cdl_option CYGDBG_INFRA_DEBUG_TRACE_BUFFER_SIZE { display "Trace buffer size" flavor data default_value 32 legal_values 5 to 65535 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." } … }
Like a cdl_package
, a cdl_component
has a name and a body. The
body contains various properties for that component, and may also
contain sub-components or options. Similarly a cdl_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 CDL commands and properties.
There is one additional and very important point: CDL is not a completely new language; instead it is implemented as an extension of the existing Tcl scripting language. The syntax of a CDL script is Tcl syntax, which is described below. In addition some of the more advanced facilities of CDL involve embedded fragments of Tcl code, for example there is a define_proc property which specifies some code that needs to be executed when the component framework generates the configuration header files.
3.2. CDL Commands
There are four CDL-related commands which can occur at the top-level
of a CDL script: cdl_package
, cdl_component
, cdl_option
and cdl_interface
. These correspond to the basic building blocks of the
language (CDL interfaces are described in Section 3.7, “Interfaces”). All of these take the same basic form:
cdl_package <name> { … } cdl_component <name> { … } cdl_option <name> { … } cdl_interface <name> { … }
The command is followed by a name and by a body of properties, the
latter enclosed in braces. Packages and components can contain other
entities, so the cdl_package
and cdl_component
can also have
nested commands 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 eCos 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 CDL script, which is specified in the
packages
ecos.db database
entry. 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 command in the top-level script should be cdl_package
, and the
name used should be the same as in the ecos.db
database. There should be only one cdl_package
command per package.
The various CDL 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.
All components, options and interfaces that are defined directly in
the top-level script will be placed below the package in the hierarchy.
Alternatively they can be nested in the body of the cdl_package
command. The following two script fragments are equivalent:
cdl_package CYGPKG_LIBC { … } cdl_component CYGPKG_LIBC_STRING { … } cdl_option CYGPKG_LIBC_CTYPE_INLINES { … }
and:
cdl_package CYGPKG_LIBC { … cdl_component CYGPKG_LIBC_STRING { … } cdl_option CYGPKG_LIBC_CTYPE_INLINES { … } }
If a script defines options both inside and outside the body of the cdl_package
then the ones inside will be processed first. Language
purists may argue that it would have been better if all contained
options and components had to go into the body, but in practice it is
often convenient to be able to skip this level of nesting and the
resulting behavior is still well-defined.
Components can also contain options and other CDL entities, in fact
that is what distinguishes them from options. These can be defined in
the body of the cdl_component
command:
cdl_component CYGPKG_LIBC_STDIO { cdl_component CYGPKG_LIBC_STDIO_FLOATING_POINT { … } cdl_option CYGSEM_LIBC_STDIO_THREAD_SAFE_STREAMS { … } }
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 CDL data into multiple CDL
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 CDL script
libc.cdl
would contain the following:
cdl_package CYGPKG_LIBC { … cdl_component CYGPKG_LIBC_STDIO { … script stdio.cdl } }
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.cdl
.
It is possible to have some options nested in the body of a cdl_component
command 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 CDL 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 CDL language support for constructing hierarchies automatically and use the parent property explicitly for every single option and component. Again this is not generally useful.
Note | |
---|---|
At the time of writing interfaces cannot act as containers. This may change in a future version of the component framework. If the change is made then interfaces would support the script property, just like components. |
3.3. CDL Properties
Each package, component, option, and interface has a body 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 commands. 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.
All property parsing code supports options for every property,
although at present the majority of properties do not yet take any
options. Any initial arguments that begin with a hyphen character
-
will be interpreted as an option, for example:
cdl_package CYGPKG_HAL_ARM { … make -priority 1 { … } }
If the option involves additional data, as for the
-priority
example above, then this can be written
as either -priority=1
or as
-priority 1
. On occasion the option parsing
code can get in the way, for example:
cdl_option CYGNUM_LIBC_TIME_DST_DEFAULT_STATE { … legal_values -1 to 1 default_value -1 }
Neither the legal_values nor the default_value property will
accept -1
as a valid option, so this will result in
syntax errors when the CDL script is read in by the component
framework. To avoid problems, the option parsing code will recognize
the string --
and will not attempt to interpret any
subsequent arguments. Hence this option should be written as:
cdl_option CYGNUM_LIBC_TIME_DST_DEFAULT_STATE { … legal_values -- -1 to 1 default_value -- -1 }
The property parsing code involves a recursive invocation of the Tcl
interpreter that is used to parse the top-level commands. This means
that some characters in the body of an option will be treated
specially. The #
character can be used for
comments. The backslash character \
, the
dollar character $
, square brackets
[
and ]
, braces
{
and }
, and the quote character
"
may all receive special treatment. Most of the
time this is not a problem because these characters are not useful for
most properties. On occasion having a Tcl interpreter around
performing the parser can be very powerful. For more details of
how the presence of a Tcl interpreter can affect CDL scripts,
see Section 3.5, “An Introduction to Tcl”.
Many of the properties can be used in any of cdl_package
, cdl_component
, cdl_option
or cdl_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 cdl_package
command. 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 Chapter 5, CDL Language Specification. Properties related to values and expressions are described in more detail in Section 3.6, “Values and Expressions”. Properties related to header file generation and to the build process are described in Chapter 4, The Build Process.
3.3.1. 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.
cdl_package CYGPKG_UITRON { display "uITRON compatibility layer" doc ref/ecos-ref.a.html 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." … }
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.
3.3.2. 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 CDL 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.
cdl_package CYGPKG_HAL_SH { display "SH architecture" parent CYGPKG_HAL … }
The parent property can also be used in the body of a cdl_component
, cdl_option
or cdl_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 CDL 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.
The script property can only be used in the body of a cdl_component
command. The property takes a single filename as
argument, and this should be another CDL script 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 cdl
subdirectory of the package, otherwise the filename will be treated as
relative to the package's top-level directory.
cdl_component CYGPKG_LIBC_STDIO { display "Standard input/output functions" flavor bool requires CYGPKG_IO requires CYGPKG_IO_SERIAL_HALDIAG default_value 1 description " This enables support for standard I/O functions from <stdio.h>." script stdio.cdl }
3.3.3. Value-related Properties
There are seven properties which are related to option values and state: flavor, calculated, default_value, legal_values, active_if, implements, and requires. More detailed information can be found in Section 3.6, “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 CDL 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 CDL flavors and how a flavor affects expression
evaluation, and other consequences, see Section 3.6, “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 CDL expression as argument, for example:
# A constant on some target hardware, perhaps user-modifiable on other # targets. cdl_option CYGNUM_HAL_RTC_PERIOD { display "Real-time clock period" flavor data calculated 12500 }
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:
cdl_option CYGDBG_HAL_DEBUG_GDB_THREAD_SUPPORT { display "Include GDB multi-threading debug support" requires CYGDBG_KERNEL_DEBUG_GDB_THREAD_SUPPORT default_value CYGDBG_KERNEL_DEBUG_GDB_THREAD_SUPPORT … }
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 CDL list expression.
cdl_option CYGNUM_LIBC_TIME_STD_DEFAULT_OFFSET { display "Default Standard Time offset" flavor data legal_values -- -90000 to 90000 default_value -- 0 … }
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
CDL goal expression.
# Do not provide extra semaphore debugging if there are no semaphores cdl_option CYGDBG_KERNEL_INSTRUMENT_BINSEM { active_if CYGPKG_KERNEL_SYNCH … }
The implements property is related to the concept of CDL interfaces. If an option is
active and enabled and it implements a particular interface then it
contributes 1
to that interface's value.
cdl_package CYGPKG_NET_EDB7XXX_ETH_DRIVERS { display "Cirrus Logic ethernet driver" implements CYGHWR_NET_DRIVERS implements CYGHWR_NET_DRIVER_ETH0 … }
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.
cdl_option CYGSEM_LIBC_PER_THREAD_ERRNO { display "Per-thread errno" doc ref/ecos-ref.15.html requires CYGVAR_KERNEL_THREADS_DATA default_value 1 … }
The arguments to the requires property should be a CDL goal expression.
3.3.4. 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 filename. This applies
to all the components and options within a package, so it can only be
used in the body of a cdl_package
command. For example the following
specifies that the configuration header file for the SPARClite HAL
package is pkgconf/hal_sparclite.h
.
cdl_package CYGPKG_HAL_SPARCLITE { display "SPARClite architecture" parent CYGPKG_HAL hardware define_header hal_sparclite.h … }
Note | |
---|---|
At present the main use for the define_header property is related to hardware packages, see the reference pages for more details. |
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.
cdl_component CYG_HAL_STARTUP { display "Startup type" flavor data legal_values { "RAM" "ROM" } default_value {"RAM"} no_define define -file system.h CYG_HAL_STARTUP … }
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.
3.3.5. 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 cdl_package
command.
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:
cdl_package CYGPKG_ERROR { display "Common error code support" compile strerror.cxx include_dir cyg/error … }
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 cdl_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 cdl_option
, cdl_component
or cdl_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:
cdl_package CYGPKG_HAL_MN10300_AM33 { display "MN10300 AM33 variant" … 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 } }
For full details of custom build steps and the build process generally, see Chapter 4, The Build Process.
By default 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:
cdl_package CYGPKG_INFRA { display "Infrastructure" include_dir cyg/infra … }
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.
3.3.6. 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.
cdl_package CYGPKG_HAL_MIPS { display "MIPS architecture" parent CYGPKG_HAL hardware include_dir cyg/hal define_header hal_mips.h … }
At present the hardware property is largely ignored by the component framework. This may change in future releases.
3.4. Option Naming Convention
All the options in a given configuration live in the same namespace. Furthermore it is not possible for two separate options to have the same name, because this would make any references to those options in CDL expressions ambiguous. A naming convention exists to avoid problems. It is recommended that component writers observe some or all of this convention to reduce the probability of name clashes with other packages.
There is an important restriction on option names. Typically the
component framework will output a #define
for every
active and enabled option, using the name as the symbol being defined.
This requires that all names are valid C preprocessor symbols, a
limitation that is enforced even for options which have the no_define property. Preprocessor symbols can be any sequence of
lower case letters a
-z
, upper
case letters, A
-Z
, the
underscore character _
, and the digits
0
-9
. The first character must be
a non-digit. Using an underscore as the first character is
discouraged, because that may clash with reserved language
identifiers. In addition there is a convention that preprocessor
symbols only use upper case letters, and some component writers may
wish to follow this convention.
A typical option name could be something like
CYGSEM_KERNEL_SCHED_BITMAP
. This name consists of
several different parts:
The first few characters, in this case the three letters
CYG
, are used to identify the organization that produced the package. For historical reasons packages produced by Red Hat tend to use the prefixCYG
rather thanRHAT
. Component writers should use their own prefix: even when cutting and pasting from an existing CDL script the prefix should be changed to something appropriate to their organization.It can be argued that a short prefix, often limited to upper case letters, is not sufficiently long to eliminate the possibility of name clashes. A longer prefix could be used, for example one based on internet domain names. However the C preprocessor has no concept of namespaces or
import
directives, so it would always be necessary to use the full option name in component source code which gets tedious - option names tend to be long enough as it is. There is a small increased risk of name clashes, but this risk is felt to be acceptable.The next three characters indicate the nature of the option, for example whether it affects the interface or just the implementation. A list of common tags is given below.
The
KERNEL_SCHED
part indicates the location of the option within the overall hierarchy. In this case the option is part of the scheduling component of the kernel package. Having the hierarchy details as part of the option name can help in understanding configurable code and further reduces the probability of a name clash.The final part,
BITMAP
, identifies the option itself.
The three-character tag is intended to provide some additional
information about the nature of the option. There are a number of
pre-defined tags. However for many options there is a choice:
options related to the platform should normally use
HWR
, but numerical options should normally use
NUM
; a platform-related numerical option such as
the size of an interrupt stack could therefore use either tag.
There are no absolute rules, and it is left to component writers to
interpret the following guidelines:
-
xxxARC_
The
ARC
tag is intended for options related to the processor architecture. Typically such options will only occur in architectural or variant HAL packages.-
xxxHWR_
The
HWR
tag is intended for options related to the specific target board. Typically such options will only occur in platform HAL packages.-
xxxPKG_
This tag is intended for packages or components, in other words options which extend the configuration hierarchy. Arguably a
COM
tag would be more appropriate for components, but this could be confusing because of the considerable number of computing terms that begin with com.-
xxxGLO_
This is intended for global configuration options, especially preferences.
-
xxxDBG_
The
DBG
tag indicates that the option is in some way related to debugging, for example it may enable assertions in some part of the system.-
xxxTST_
This tag is for testing-related options. Typically these do not affect actual application code, instead they control the interaction between target-side test cases and a host-side testing infrastructure.
-
xxxFUN_
This is for configuration options which affect the interface of a package. There are a number of related tag which are also interface-related.
xxxFUN_
is intended primarily for options that control whether or not one or more functions are provided by the package, but can also be used if none of the other interface-related tags is applicable.-
xxxVAR_
This is analogous to
FUN
but controls the presence or absence of one or more variables or objects.-
xxxCLS_
The
CLS
tag is intended only for packages that provide an object-oriented interface, and controls the presence or absence of an entire class.-
xxxMFN_
This is also for object-orientated interfaces, and indicates the presence or absence of a member function rather than an entire class.
-
xxxSEM_
A
SEM
option does not affect the interface (or if does affect the interface, this is incidental). Instead it is used for options which have a fundamental effect on the semantic behavior of a package. For example the choice of kernel schedulers is semantic in nature: it does not affect the interface, in particular the functioncyg_thread_create
exists irrespective of which scheduler has been selected. However it does have a major impact on the system's behavior.-
xxxIMP_
IMP
is for implementation options. These do not affect either the interface or the semantic behavior (with the possible exception of timing-related changes). A typical implementation option controls whether or not a particular function or set of functions should get inlined.-
xxxNUM_
This tag is for numerical options, for example the number of scheduling priority levels.
-
xxxDAT_
This is for data items that are not numerical in nature, for example a device name.
-
xxxBLD_
The
BLD
tag indicates an option that affects the build process, for example compiler flag settings.-
xxxINT_
This should normally be used for CDL interfaces, which is a language construct that is largely independent from the interface exported by a package via its header files. For more details of CDL interfaces see Section 3.7, “Interfaces”.
-
xxxPRI_
This tag is not normally used for configuration options. Instead it is used by CDL scripts to pass additional private information to the source code via the configuration header files, typically inside a define_proc property.
-
xxxSRC_
This tag is not normally used for configuration options. Instead it can be used by package source code to interact with such options, especially in the context of the if_define property.
There is one special case of a potential name clash that is worth
mentioning here. When the component framework generates a
configuration header file for a given package, by default it will use
a name derived from the package name (the define_header property can
be used to override this). The file name is constructed from the
package name by removing everything up to and including the first
underscore, converting the remainder of the name to lower case, and
appending a .h
suffix. For example the kernel
package CYGPKG_KERNEL
will involve a header file
pkgconf/kernel.h
. If a
configuration contained some other package
XYZPKG_KERNEL
then this would attempt to use the
same configuration header file, with unfortunate effects. Case
sensitivity could introduce problems as well, so a package
xyzpkg_kernel
would involve the same problem. Even
if the header file names preserved the case of the package name, not
all file systems are case sensitive. There is no simple solution to
this problem. Changing the names of the generated configuration header
files would involve a major incompatible change to the interface, to
solve a problem which is essentially hypothetical in nature.
3.5. An Introduction to Tcl
All CDL scripts are implemented as Tcl scripts, and are read in by
running the data through a standard Tcl interpreter, extended with a
small number of additional commands such as
cdl_option
and cdl_component
.
Often it is not necessary to know the full details of Tcl syntax.
Instead it is possible to copy an existing script, perform some copy
and paste operations, and make appropriate changes to names and to
various properties. However there are also cases where an
understanding of Tcl syntax is very desirable, for example:
cdl_option CYGDAT_UITRON_MEMPOOLFIXED_EXTERNS { display "Externs for initialization" flavor data default_value {"static char fpool1[ 2000 ], \\\n\ fpool2[ 2000 ], \\\n\ fpool3[ 2000 ];"} … }
This causes the cdl_option
command to be executed, which in turn
evaluates its body in a recursive invocation of the Tcl interpreter.
When the default_value property is encountered the braces around the
value part are processed by the interpreter, stopping it from doing
further processing of the braced contents (except for backslash
processing at the end of a line, that is special). In particular it
prevents command substitution for
[ 2000 ]
. A single argument will be
passed to the default_value command which expects a CDL
expression, so the expression parsing code is passed the following:
"static char fpool1[ 2000 ], \\\n fpool2[ 2000 ], \\\n fpool3[ 2000 ];"
The CDL expression parsing code will treat this as a simple string constant, as opposed to a more complicated expression involving other options and various operators. The string parsing code will perform the usual backslash substitutions so the actual default value will be:
static char fpool1[ 2000 ], \ fpool2[ 2000 ], \ fpool3[ 2000 ];
If the user does not modify the option's value then the following will be generated in the appropriate configuration header file:
#define CYGDAT_UITRON_MEMPOOLFIXED_EXTERNS static char fpool1[ 2000 ], \ fpool2[ 2000 ], \ fpool3[ 2000 ];
Getting this desired result usually requires an understanding of both Tcl syntax and CDL expression syntax. Sometimes it is possible to substitute a certain amount of trial and error instead, but this may prove frustrating. It is also worth pointing out that many CDL scripts do not involve this level of complexity. On the other hand, some of the more advanced features of the CDL language involve fragments of Tcl code, for example the define_proc property. To use these component writers will need to know about the full Tcl language as well as the syntax.
Although the current example may seem to suggest that Tcl is rather complicated, it is actually a very simple yet powerful scripting language: the syntax is defined by just eleven rules. On occasion this simplicity means that Tcl's behavior is subtly different from other languages, which can confuse newcomers.
When the Tcl interpreter is passed some data such as
puts Hello
, it splits this data into a command
and its arguments. The command will be terminated by a newline or by a
semicolon, unless one of the quoting mechanisms is used. The command
and each of its arguments are separated by white space. So in the
following example:
puts Hello set x 42
This will result in two separate commands being executed. The first
command is puts
and is passed a single argument,
Hello
. The second command is set
and is passed two arguments, x
and
42
. The intervening newline character serves to
terminate the first command, and a semi-colon separator could be used
instead:
puts Hello;set x 42
Any white space surrounding the semicolon is just ignored because it does not serve to separate arguments.
Now consider the following:
set x Hello world
This is not valid Tcl. It is an attempt to invoke the
set
command with three arguments:
x
, Hello
, and
world
. The set
only takes two
arguments, a variable name and a value, so it is necessary to combine
the data into a single argument by quoting:
set x "Hello world"
When the Tcl interpreter encounters the first quote character it
treats all subsequent data up to but not including the closing quote
as part of the current argument. The quote marks are removed by the
interpreter, so the second argument passed to the
set
command is just Hello world
without the quote characters. This can be significant in the context
of CDL scripts. For example:
cdl_option CYG_HAL_STARTUP { … default_value "RAM" }
The Tcl interpreter strips off the quote marks so the CDL
expression parsing code sees RAM
instead of
"RAM"
. It will treat this as a reference to
some unknown option RAM
rather than as a string
constant, and the expression evaluation code will use a value of
0
when it encounters an option that is not
currently loaded. Therefore the option
CYG_HAL_STARTUP
ends up with a default value of
0
. Either braces or backslashes should be used to
avoid this, for example
default_value { "RAM" }
.
Note | |
---|---|
There are long-term plans to implement some sort of CDL validation utility cdllint which could catch common errors like this one. |
A quoted argument continues until the closing quote character is encountered, which means that it can span multiple lines. Newline or semicolon characters do not terminate the current command in such cases. description properties usually make use of this:
cdl_package CYGPKG_ERROR { 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." … }
The Tcl interpreter supports much the same forms of backslash
substitution as other common programming languages. Some backslash
sequences such as \n
will be replaced by the
appropriate character. The sequence \\
will be
replaced by a single backslash. A backslash at the very end of a line
will cause that backslash, the newline character, and any white space
at the start of the next line to be replaced by a single space. Hence
the following two Tcl commands are equivalent:
puts "Hello\nworld\n" puts \ "Hello world "
If a description string needs to contain quote marks or other
special characters then backslash escapes can be used. In addition to
quote and backslash characters, the Tcl interpreter treats square
brackets, the $
character, and braces specially.
Square brackets are used for command substitution, for example:
puts "The answer is [expr 6 * 9]"
When the Tcl interpreter encounters the square brackets it will treat
the contents as another command that should be executed first, and the
result of executing that is used when continuing to process the
script. In this case the Tcl interpreter will execute the command
expr 6 * 9
, yielding a result of 42
[1]
and then the
Tcl interpreter will execute puts "The answer is 42"
.
It should be noted that the interpreter performs only one level
of substitution: if the result of performing command substitution
performs further special characters such as square brackets then these
will not be treated specially.
Command substitution will not prove useful for many CDL scripts, except for e.g. a define_proc property which involves a fragment of Tcl code. Potentially there are some interesting uses, for example to internationalize display strings. However care does have to be taken to avoid unexpected command substitution, for example if an option description involves square brackets then typically these would require backslash-escapes.
The $
character is used in Tcl scripts to perform
variable substitution:
set x [expr 6 * 9] puts "The answer is $x"
Variable substitution, like command substitution, is unlikely to
prove useful for many CDL scripts except in the context of Tcl fragments. If it is necessary to have a $
character then a backslash escape may have to be used.
Braces are used to collect a sequence of characters into a single argument, just like quotes. The difference is that variable, command and backslash substitution do not occur inside braces (with the sole exception of backslash substitution at the end of a line). Therefore given a line in a CDL script such as:
default_value {"RAM"}
The braces are stripped off by the Tcl interpreter, leaving
"RAM"
which will be handled as a string constant by
the expression parsing code. The same effect could be achieved using
one of the following:
default_value \"RAM\" default_value "\"RAM\""
Generally the use of braces is less confusing. At this stage it is worth noting that the basic format of CDL data makes use of braces:
cdl_option <name> { … };
The cdl_option
command is passed two arguments, a name and a body,
where the body consists of everything inside the braces but not the
braces themselves. This body can then be executed in a recursive
invocation of the Tcl interpreter. If a CDL script contains
mismatched braces then the interpreter is likely to get rather
confused and the resulting diagnostics may be difficult to understand.
Comments in Tcl scripts are introduced by a hash character
#
. However, a hash character only introduces a
comment if it occurs where a command is expected. Consider the
following:
# This is a comment puts "Hello" # world
The first line is a valid comment, since the hash character occurs
right at the start where a command name is expected. The second line
does not contain a comment. Instead it is an attempt to invoke the
puts
command with three arguments:
Hello
, #
and
world
. These are not valid arguments for the
puts
command so an error will be raised.
If the second line was rewritten as:
puts "Hello"; # world
then this is a valid Tcl script. The semicolon identifies the end of the current command, so the hash character occurs at a point where the next command would start and hence it is interpreted as the start of a comment.
This handling of comments can lead to subtle behavior. Consider the following:
cdl_option WHATEVER { # This is a comment } default_value 0 … }
Consider the way the Tcl interpreter processes this. The command name
and the first argument do not pose any special difficulties. The
opening brace is interpreted as the start of the next argument, which
continues until a closing brace is encountered. In this case the
closing brace occurs on the second line, so the second argument passed
to cdl_option
is
\n # This is a comment
. This second argument is processed in a recursive
invocation of the Tcl interpreter and does not contain any commands,
just a comment. Top-level script processing then resumes, and the next
command that is encountered is default_value
. Since
the parser is not currently processing a configuration option this is
an error. Later on the Tcl interpreter would encounter a closing brace
by itself, which is also an error.
For component writers who need more information about Tcl, especially about the language rather than the syntax, various resources are available. A reasonable starting point is the Scriptics developer web site.
3.6. 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 CDL things are not so straightforward.
3.6.1. Option Values
There are four factors which go into an option's value:
An option may or may not be loaded.
If the option is loaded, it may or may not be active.
Even if the option is active, it may or may not be enabled.
If the option is loaded, active and enabled then it will have some associated data which constitutes its value.
3.6.1.1. 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 CDL expressions can reference options in other packages, and often need to do so, it is essential to define the resulting behavior.
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. Even for packages which have been installed, the current
repository database does not hold details of every option, only of the
packages themselves. If a CDL 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. If the
package is not loaded then theoretically the framework might guess
that the option is somehow related to the kernel by examining the
option name but this would not be robust: the option could easily be
part of some other package that violates the naming convention.
Assume that the user is building a minimal configuration which does not contain the kernel package, but does have other packages which contain the following constraints:
requires CYGPKG_KERNEL requires CYGPKG_KERNEL_THREADS_DATA requires !CYGSEM_KERNEL_SCHED_TIMESLICE
Clearly the first constraint is not satisfied because the kernel is not loaded. The second constraint is also not satisfied. The third constraint is trivially satisfied: if there is no kernel then the kernel's timeslicing support cannot possibly be enabled.
Any options which are not in the current configuration are handled as follows:
Any references to that option will evaluate to
0
, sorequires !CYGSEM_KERNEL_SCHED_TIMESLICE
will be satisfied butrequires CYGSEM_KERNEL_THREADS_DATA
will not be satisfied.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 CDL expressions.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.
3.6.1.2. 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:
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; sinceCYGPKG_LIBC_STDIO_FLOATING_POINT
is now inactive,CYGSEM_LIBC_STDIO_PRINTF_FLOATING_POINT
is inactive as well.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.
If an option is inactive then any references to that option in CDL expressions will evaluate to
0
. Hence a constraint of the formrequires CYGSEM_LIBC_STDIO_PRINTF_FLOATING_POINT
is not satisfied if the entire stdio component is disabled.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.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 dependencyrequires 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 onCYGPKG_LIBM
.
3.6.1.3. 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. CDL has to cope with this variety, and define the exact
behavior of the system in terms of constraints and build-time
consequences.
In CDL 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:
If an option is disabled, in other words if the boolean part is false, then any references to that option in CDL expressions will evaluate to
0
. This is the same behavior as for inactive options. The data part is not relevant. Thenone
anddata
flavors specify that the option is always enabled, in which case this rule is not applicable.If an option is enabled then any references to that option in CDL expressions will evaluate to the option's data part. For two of the flavors,
none
andbool
, this data part is fixed to the constant1
which generally has the expected result.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.
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
andbooldata
flavors. Options with thenone
anddata
flavors are always enabled so their constraints always have to be satisfied (assuming the option is active).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 CDL scripts. 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.
CDL Flavors | |
---|---|
The concept of CDL flavors tends to result in various discussions about why it is unnecessarily complicated, and would it not have been easier to do … However there are very good reasons why CDL works the way it does. The first common suggestion is that there is no need to have separate
flavors
Although initially it may seem confusing that an option's value has both a boolean and a data part, it is an accurate reflection of how configuration options actually work. The various alternatives would all make it harder to write CDL scripts. The next common suggestion is that the data part of a value should be
typed in much the same way as C or C++ data types. For example it
should be possible to describe
The CDL approach of treating all data as a sequence of characters, possibly constrained by a legal_values property or other means, has the great advantage of simplicity. It also fits in with the Tcl language that underlies CDL. |
3.6.1.4. Some Examples
The following excerpt from the C library's CDL scripts can be used to illustrate how values and flavors work in practice:
cdl_component CYGPKG_LIBC_RAND { flavor none compile stdlib/rand.cxx cdl_option CYGSEM_LIBC_PER_THREAD_RAND { requires CYGVAR_KERNEL_THREADS_DATA default_value 0 } cdl_option CYGNUM_LIBC_RAND_SEED { flavor data legal_values 0 to 0x7fffffff default_value 1 } cdl_option CYGNUM_LIBC_RAND_TRACE_LEVEL { flavor data legal_values 0 to 1 default_value 0 } }
If the application does not require any C library functionality then
it is possible to have a configuration where the C library is not
loaded. This can be achieved by starting with the minimal template, or
by starting with another template such as the default one and then
explicitly unloading the C library package. If this package is not
loaded then any references to the CYGPKG_LIBC_RAND
component or any of its options will have a value of
0
for the purposes of expression evaluation. No
#define's
will be generated for the component or
any of its options, and the file stdlib/rand.cxx
will not get compiled. There is nothing special about the C library
here, exactly the same would apply for say a device driver that does
not correspond to any of the devices on the target hardware.
Assuming the C library is loaded, the next thing to consider is
whether or not the component and its options are active. The component
is layered immediately below the C library package itself, so if the
package is loaded then it is safe to assume that the package is also
enabled. Therefore the parent of CYGPKG_LIBC_RAND
is active and enabled, and in the absence of any active_if
properties CYGPKG_LIBC_RAND
will be active as well.
The component CYGPKG_LIBC_RAND
has the flavor
none
. This means the component cannot be disabled.
Therefore all the options in this component have an active and enabled
parent, and in the absence of any active_if properties they are all
active as well.
The component's flavor none
serves to group
together all of the configuration options related to random number
generation. This is particularly useful in the context of the
graphical configuration tool, but it also helps when it comes to
naming the options: all of the options begin with
CYGxxx_LIBC_RAND
, giving a clear hint about both
the package and the component within that package. The flavor means
that the component is always enabled and has the value
1
for the purposes of expression evaluation. There
will always be a single #define
of the form:
#define CYGPKG_LIBC_RAND 1
In addition the file stdlib/rand.cxx
will always
get built. If the component had the default bool
flavor then users would be able to disable the whole component,
and one less file would need to be built. However random number
generation is relatively simple, so the impact on eCos build times are
small. Furthermore by default the code has no dependencies on other
parts of the system, so compiling the code has no unexpected side
effects. Even if it was possible to disable the component, the
sensible default for most applications would still leave it enabled.
The net result is that the flavor none
is probably
the most sensible one for this component. For other components the
default bool
flavor or one of the other flavors
might be more appropriate.
Next consider option CYGSEM_LIBC_PER_THREAD_RAND
which can be used to get a per-thread random number seed, possibly
useful if the application needs a consistent sequence of random
numbers. In the absence of a flavor property this option will be
boolean, and the default_value property means that it is disabled by
default — reasonable since few applications need this
particular functionality, and it does impose a constraint on the rest
of the system. If the option is left disabled then no
#define
will be generated, and if there were any compile or similar properties these would not take effect. If the
option is enabled then a #define
will be generated,
using the option's data part which is fixed at 1
:
#define CYGSEM_LIBC_PER_THREAD_RAND 1
The CYGSEM_LIBC_PER_THREAD_RAND
option has a requires constraint on
CYGVAR_KERNEL_THREADS_DATA
. If the C library option
is enabled then the constraint should be satisfied, or else the
configuration contains a conflict. If the configuration does not
include the kernel package then
CYGVAR_KERNEL_THREADS_DATA
will evaluate to
0
and the constraint is not satisfied. Similarly if
the option is inactive or disabled the constraint will not be
satisfied.
CYGNUM_LIBC_RAND_SEED
and
CYGNUM_LIBC_RAND_TRACE_LEVEL
both have the
data
flavor, so they are always enabled and the
component framework will generate appropriate
#define's
:
#define CYGNUM_LIBC_RAND_SEED 1 #define CYGNUM_LIBC_RAND_SEED_1 #define CYGNUM_LIBC_RAND_TRACE_LEVEL 0 #define CYGNUM_LIBC_RAND_TRACE_LEVEL_0
Neither option has a compile or similar property, but any such
properties would take effect. Any references to these options in CDL
expressions would evaluate to the data part, so a hypothetical
constraint of the form
{ requires CYGNUM_LIBC_RAND_SEED > 42 }
would not be satisfied with the default values. Both options use a
simple constant for the default_value expression. It would be
possible to use a more complicated expression, for example the default
for CYGNUM_LIBC_RAND_TRACE_LEVEL
could be
determined from some global debugging option or from a debugging
option that applies to the C library as a whole. Both options also
have a legal_values constraint, which must be satisfied since the
options are active and enabled.
Note | |
---|---|
The value |
3.6.2. Ordinary Expressions
Expressions in CDL follow a conventional syntax, for example:
default_value CYGGLO_CODESIZE > CYGGLO_SPEED default_value { (CYG_HAL_STARTUP == "RAM" && !CYGDBG_HAL_DEBUG_GDB_INCLUDE_STUBS && !CYGINT_HAL_USE_ROM_MONITOR_UNSUPPORTED && !CYGSEM_HAL_POWERPC_COPY_VECTORS) ? 1 : 0 } default_value { "\"/dev/ser0\"" }
However there is a complication in that the various arguments to a default_value property will first get processed by a Tcl interpreter, so special characters like quotes and square brackets may get processed. Such problems can be avoided by enclosing non-trivial expressions in braces, as in the second example above. The way expression parsing actually works is as follows:
The Tcl interpreter splits the line or lines into a command and its arguments. In the first default_value expression above the command is
default_value
and there are three arguments,CYGGLO_CODESIZE
,>
andCYGGLO_SPEED
. In the second and third examples there is just one argument, courtesy of the braces.Next option processing takes place, so any initial arguments that begin with a hyphen will be interpreted as options. This can cause problems if the expression involves a negative number, so the special argument
--
can be used to prevent option processing on the subsequent arguments.All of the arguments are now concatenated, with a single space in between each one. Hence the following two expressions are equivalent, even though they will have been processed differently up to this point.
default_value CYGGLO_CODESIZE > CYGGLO_SPEED default_value {CYGGLO_CODESIZE > CYGGLO_SPEED}
The expression parsing code now has a single string to process.
CDL expressions consist of four types of element: references to
configuration options, constant strings, integers, and floating point
numbers. These are combined using a conventional set of operators: the
unary operators -
, ~
and
!
; the arithmetic operators +
,
-
, *
, /
and
%
; the shift operators <<
and >>
; the comparison operators
==
, !=
, <
,
<=
, >
and
>=
; the bitwise operators
&
, ^
and
|
; the logical operators &&
and
||
; the string concatenation operator
.
; and the ternary conditional operator
A ? B : C
. There is also support for
some less widely available operators for logical equivalence and
implication, and for a set of function-style operations. Bracketed
sub-expressions are supported, and the operators have the usual
precedence:
Priority | Operators | Category |
---|---|---|
16 | references, constants | basic elements |
15 |
f(a, b, c)
| function calls |
14 |
~
| bitwise not |
14 |
!
| logical not |
14 |
-
| arithmetic negation |
13 |
* / %
| multiplicative arithmetic |
12 |
+ - .
| additive arithmetic and string concatenation |
11 |
<< >>
| bitwise shifts |
10 |
<= < > >=
| inequality |
9 |
== !=
| comparison |
8 |
&
| bitwise and |
7 |
^
| bitwise xor |
6 |
|
| bitwise or |
5 |
&&
| logical and |
4 |
||
| logical or |
3 |
xor, eqv
| logical equivalance |
2 |
implies
| logical implication |
1 |
? :
| conditional |
Function calls have the usual format of a name, an opening bracket, one or more arguments separated by commas, and a closing bracket. For example:
requires { !is_substr(CYGBLD_GLOBAL_CFLAGS, " -fno-rtti") }
Functions will differ in the number of arguments and may impose restrictions on some or all of their arguments. For example it may be necessary for the first argument to be a reference to a configuration option. The available functions are described in Section 3.6.3, “Functions”.
The logical xor
operator evaluates to true if
either the left hand side or the right hand side but not both evaluate
to true The logical eqv
operator evaluates to true
if both the left and right hand sides evaluate to true, or if both
evaluate to false. The implies
operator evaluates
to true either if the left hand side is false or if the right hand
side is true, in other words A implies B
has the same meaning as !A || B
. An
example use would be:
requires { is_active(CYGNUM_LIBC_MAIN_DEFAULT_STACK_SIZE) implies (CYGNUM_LIBC_MAIN_DEFAULT_STACK_SIZE >= (16 * 1024)) }
This constraint would be satisfied if either the support for a main stack size is disabled, or if that stack is at least 16K. However if such a stack were in use but was too small, a conflict would be raised.
A valid CDL identifier in an expression, for example
CYGGLO_SPEED
, will be interpreted as a reference to
a configuration option by that name. The option does not have to be
loaded into the current configuration. When the component framework
evaluates the expression it will substitute in a suitable value that
depends on whether or not the option is loaded, active, and enabled.
The exact rules are described in Section 3.6.1, “Option Values”.
A constant string is any sequence of characters enclosed in quotes. Care has to be taken that these quotes are not stripped off by the Tcl interpreter before the CDL expression parser sees them. Consider the following:
default_value "RAM"
The quote marks will be stripped before the CDL expression parser
sees the data, so the expression will be interpreted as a reference to
a configuration option RAM
. There is unlikely to be
such an option, so the actual default value will be
0
. Careful use of braces or other Tcl quoting
mechanisms can be used to avoid such problems.
String constants consist of the data inside the quotes. If the data itself needs to contain quote characters then appropriate quoting is again necessary, for example:
default_value { "\"/dev/ser0\"" }
An integer constant consists of a sequence of digits, optionally
preceeded with the unary +
or -
operators. As usual the sequence 0x
or
0X
can be used for hexadecimal data, and a leading
0
indicates octal data. Internally the component
framework uses 64-bit arithmetic for integer data. If a constant is
too large then double precision arithmetic will be used instead.
Traditional syntax is also used for double precision numbers, for
example 3.141592
or -3E6
.
Of course this is not completely accurate: CDL is not a typed language, all data is treated as if it were a string. For example the following two lines are equivalent:
requires CYGNUM_UITRON_SEMAS > 10 requires { CYGNUM_UITRON_SEMAS > "10" }
When an expression gets evaluated the operators will attempt
appropriate conversions. The >
comparison
operator can be used on either integer or double precision numbers, so
it will begin by attempting a string to integer conversion of both
operands. If that fails it will attempt string to double conversions.
If that fails as well then the component framework will report a
conflict, an evaluation exception. If the conversions from string to
integer are successful then the result will be either the string
0
or the string 1
, both of which
can be converted to integers or doubles as required.
It is worth noting that the expression
CYGNUM_UITRON_SEMAS >10
is not ambiguous. CDL identifiers can never begin with a digit, so it is not possible
for 10
to be misinterpreted as a reference to an
identifier instead of as a string.
Of course the implementation is slightly different again. The CDL
language definition is such that all data is treated as if it were a
string, with conversions to integer, double or boolean as and when
required. The implementation is allowed to avoid conversions until
they are necessary. For example, given
CYGNUM_UITRON_SEMAS > 10
the
expression parsing code will perform an immediate conversion from
string to integer, storing the integer representation, and there is no
need for a conversion by the comparison operator when the expression
gets evaluated. Given
{ CYGNUM_UITRON_SEMAS > "10" }
the parsing code will store the string representation and a conversion
happens the first time the expression is evaluated. All of this is an
implementation detail, and does not affect the semantics of the
language.
Different operators have different requirements, for example the bitwise or operator only makes sense if both operands have an integer representation. For operators which can work with either integer or double precision numbers, integer arithmetic will be preferred.
The following operators only accept integer operands:
unary ~
(bitwise not), the shift operators
<<
and >>
, and the
bitwise operators &
, |
and
^
.
The following operators will attempt integer arithmetic first, then
double precision arithmetic: unary -
,
the arithmetic operators +
, -
,
*
, /
, and %
;
and the comparision operators <
,
<=
, >
and
>=
.
The equality ==
and inequality
!=
operators will first attempt integer conversion
and comparison. If that fails then double precision will be attempted
(although arguably using these operators on double precision data is
not sensible). As a last resort string comparison will be used.
The operators !
, &&
and
||
all work with boolean data. Any string that can
be converted to the integer 0
or the double
0.0
is treated as false, as is the empty string or
the constant string false
. Anything else is
interpreted as true. The result is either 0
or
1
.
The conditional operator ? :
will interpret
its first operand as a boolean. It does not perform any processing on
the second or third operands.
In practice it is rarely necessary to worry about any of these details. In nearly every case CDL expressions just work as expected, and there is no need to understand the full details.
Note | |
---|---|
The current expression syntax does not meet all the needs of component writers. Some future enhancements will definitely be made, others are more controversial. The list includes the following:
|
Note | |
---|---|
The current implementation of the component framework uses 64 bit arithmetic on all host platforms. Although this is adequate for current target architectures, it may cause problems in future. At some stage it is likely that an arbitrary precision integer arithmetic package will be used instead. |
3.6.3. Functions
CDL expressions can contain calls to a set of built-in functions using the usual syntax, for example;
requires { !is_substr(CYGBLD_GLOBAL_CFLAGS, "-fno-rtti") }
The available function calls are as follows:
-
get_data(option)
This function can be used to obtain just the data part of a loaded configuration option, ignoring other factors such as whether or not the option is active and enabled. It takes a single argument which should be the name of a configuration option. If the specified option is not loaded in the current configuration then the function returns 0, otherwise it returns the data part. Typically this function will only be used in conjunction with
is_active
andis_enabled
for fine-grained control over the various factors that make up an option's value.-
is_active(option)
This function can be used to determine whether or not a particular configuration option is active. It takes a single argument which should be the name of an option, and returns a boolean. If the specified option is not loaded then the function will return false. Otherwise it will consider the state of the option's parents and evaluate any active_if properties, and return the option's current active state. A typical use might be:
requires { is_active(CYGNUM_LIBC_MAIN_DEFAULT_STACK_SIZE) implies (CYGNUM_LIBC_MAIN_DEFAULT_STACK_SIZE >= (16 * 1024)) }
In other words either the specified configuration option must be inactive, for example because the current application does not use any related C library or POSIX functionality, or the stack size must be at least 16K.
The configuration system's inference engine can attempt to satisfy constraints involving
is_active
in various different ways, for example by enabling or disabling parent components, or by examining active_if properties and manipulating terms in the associated expressions.-
is_enabled(option)
This function can be used to determine whether or not a particular configuration option is enabled. It takes a single argument which should be the name of an option, and returns a boolean. If the specified option is not loaded then the function will return false. Otherwise it will return the current boolean part of the option's value. The option's active or inactive state is ignored. Typically this function will be used in conjunction with
is_active
and possiblyget_data
to provide fine-grained control over the various factors that make up an option's value.-
is_loaded(option)
This function can be used to determine whether or not a particular configuration option is loaded. It takes a single argument which should be the name of an option, and returns a boolean. If the argument is a package then the
is_loaded
function provides little or no extra information, for example the following two constraints are usually equivalent:requires { CYGPKG_KERNEL } requires { is_loaded(CYGPKG_KERNEL) }
However if the specified package is loaded but re-parented below a disabled component, or inactive as a result of an active_if property, then the first constraint would not be satisfied but the second constraint would. In other words the
is_loaded
makes it possible to consider in isolation one of the factors that are considered when CDL expressions are evaluated.The configuration system's inference engine will not automatically load or unload packages to satisfy
is_loaded
constraints.-
is_substr(haystack, needle)
This can be used to check whether or not a particular string is present in another string. It is used mainly for manipulating compiler flags. The function takes two arguments, both of which can be arbitrary expressions, and returns a boolean.
is_substr
has some understanding of word boundaries. If the second argument starts with a space character then that will match either a real space or the start of the string. Similarly if the second argument ends with a space character then that will match a real space or the end of the string. For example, all of the following conditions are satisfied:is_substr("abracadabra", "abra") is_substr("abracadabra", " abra") is_substr("hocus pocus", " pocus") is_substr("abracadabra", "abra ")
The first is an exact match. The second is a match because the leading space matches the start of the string. The third is an exact match, with the leading space matching an actual space. The fourth is a match because the trailing space matches the end of the string. However, the following condition is not satisfied.
is_substr("abracadabra", " abra ")
This fails to match at the start of the string because the trailing space is not matched by either a real space or the end of the string. Similarly it fails to match at the end of the string.
If a constraint involving
is_substr
is not satisfied and the first argument is a reference to a configuration option, the inference engine will attempt to modify that option's value. This can be achieved either by appending the second argument to the current value, or by removing all occurrences of that argument from the current value.requires { !is_substr(CYGBLD_GLOBAL_CFLAGS, " -fno-rtti ") } requires { is_substr(CYGBLD_GLOBAL_CFLAGS, " -frtti ") }
When data is removed the leading and trailing spaces will be left. For example, given an initial value of <
CYGBLD_GLOBAL_CFLAGS
of-g -fno-rtti -O2
the result will be-g -O2
rather than-g-O2
.If exact matches are needed, the function
is_xsubstr
can be used instead.-
is_xsubstr(haystack, needle)
This function checks whether or not the pattern string is an exact substring of the string being searched. It is similar to
is_substr
but uses exact matching only. In other words, leading or trailing spaces have to match exactly and will not match the beginning or end of the string being searched. The function takes two arguments, both of which can be arbitrary expressions, and returns a boolean. The difference betweenis_substr
andis_xsubstr
is illustrated by the following examples:cdl_option MAGIC { flavor data default_value { "abracadabra" } } … requires { is_substr(MAGIC, " abra") } requires { is_xsubstr(MAGIC, " abra") }
The first goal will be satisfied because the leading space in the pattern matches the beginning of the string. The second goal will not be satisfied initialy because there is no exact match, so the inference engine is likely to update the value of
MAGIC
toabracadabra abra
which does give an exact match.-
version_cmp(A, B)
This function is used primarily to check that a sufficiently recent version of some other package is being used. It takes two arguments, both of which can be arbitrary expressions. In practice usually one of the arguments will be a reference to a package and the other will be a constant version string. The return value is -1 if the first argument is a more recent version then the second, 0 if the two arguments correspond to identical versions, and 1 if the first argument is an older version. For example the following constraint can be used to indicate that the current package depends on kernel functionality that only became available in version 1.3:
requires { version_cmp(CYGPKG_KERNEL, "v1.3") <= 0 }
Note | |
---|---|
At this time it is not possible to define new functions inside a CDL script. Instead functions can only be added at the C++ level, usually by extending libcdl itself. This is partly because there is more to CDL functions than simple evaluation: associated with most functions is support for the inference engine, so that if a constraint involving a function is not currently satisfied the system may be able to find a solution automatically. |
3.6.4. Goal Expressions
The arguments to certain properties, notably requires and active_if, constitute a goal expression. As with an ordinary expression, all of the arguments get combined and then the expression parser takes over. The same care has to be taken with constant strings and anything else that may get processed by the Tcl interpreter, so often a goal expression is enclosed entirely in braces and the expression parsing code sees just a single argument.
A goal expression is basically just a sequence of ordinary expressions, for example:
requires { CYGDBG_HAL_DEBUG_GDB_INCLUDE_STUBS !CYGDBG_HAL_DEBUG_GDB_BREAK_SUPPORT !CYGDBG_HAL_DEBUG_GDB_CTRLC_SUPPORT }
This consists of three separate expressions, all of which should evaluate to a non-zero result. The same expression could be written as:
requires { CYGDBG_HAL_DEBUG_GDB_INCLUDE_STUBS && !CYGDBG_HAL_DEBUG_GDB_BREAK_SUPPORT && !CYGDBG_HAL_DEBUG_GDB_CTRLC_SUPPORT }
Alternatively the following would have much the same effect:
requires CYGDBG_HAL_DEBUG_GDB_INCLUDE_STUBS requires !CYGDBG_HAL_DEBUG_GDB_BREAK_SUPPORT requires !CYGDBG_HAL_DEBUG_GDB_CTRLC_SUPPORT
Selecting between these alternatives is largely a stylistic choice. The first is slightly more concise than the others. The second is more likely to appeal to mathematical purists. The third is more amenable to cutting and pasting.
The result of evaluating a goal expression is a boolean. If any part
of the goal expression evaluates to the integer 0
or an equivalent string then the result is false, otherwise it is
true.
The term “goal expression” relates to the component framework's inference engine: it is a description of a goal that should be satisfied for a conflict-free configuration. If a requires constraint is not satisfied then the inference engine will examine the goal expression: if there is some way of changing the configuration that does not introduce new conflicts and that will cause the goal expression to evaluate to true, the conflict can be resolved.
The inference engine works with one conflict and hence one goal expression at a time. This means that there can be slightly different behavior if a constraint is specified using a single requires property or several different ones. Given the above example, suppose that none of the three conditions are satisfied. If a single goal expression is used then the inference engine might be able to satisfy only two of the three parts, but since the conflict as a whole cannot be resolved no part of the solution will be applied. Instead the user will have to resolve the entire conflict. If three separate goal expressions are used then the inference engine might well find solutions to two of them, leaving less work for the user. On the other hand, if a single goal expression is used then the inference engine has a bit more information to work with, and it might well find a solution to the entire conflict where it would be unable to find separate solutions for the three parts. Things can get very complicated, and in general component writers should not worry about the subtleties of the inference engine and how to manipulate its behavior.
It is possible to write ambiguous goal expressions, for example:
requires CYGNUM_LIBC_RAND_SEED -CYGNUM_LIBC_RAND_TRACE_LEVEL > 5
This could be parsed in two ways:
requires ((CYGNUM_LIBC_RAND_SEED - CYGNUM_LIBC_RAND_TRACE_LEVEL) > 5) requires CYGNUM_LIBC_RAND_SEED && ((-CYGNUM_LIBC_RAND_TRACE_LEVEL) > 5)
The goal expression parsing code will always use the largest ordinary expression for each goal, so the first interpretation will be used. In such cases it is a good idea to use brackets and avoid possible confusion.
3.6.5. List Expressions
The arguments to the legal_values property constitute a goal expression. As with an ordinary and goal expressions, all of the arguments get combined and then the expression parser takes over. The same care has to be taken with constant strings and anything else that may get processed by the Tcl interpreter, so often a list expression is enclosed entirely in braces and the expression parsing code sees just a single argument.
Most list expressions take one of two forms:
legal_values <expr1> <expr2> <expr3> ... legal_values <expr1> to <expr2>
expr1
, expr2
and so on are
ordinary expressions. Often these will be constants or references to
calculated options in the architectural HAL package, but it is
possible to use arbitrary expressions when necessary. The first syntax
indicates a list of possible values, which need not be numerical. The
second syntax indicates a numerical range: both sides of the
to
must evaluate to a numerical value; if either
side involves a floating point number then any floating point number
in that range is legal; otherwise only integer values are legal;
ranges are inclusive, so 4
is a valid value given a
list expression 1 to
; if one or both
sides of the to
does not evaluate to a numerical
value then this will result in a run-time conflict. The following
examples illustrate these possibilities:
legal_values { "red" "green" "blue" } legal_values 1 2 4 8 16 legal_values 1 to CYGARC_MAXINT legal_values 1.0 to 2.0
It is possible to combine the two syntaxes, for example:
legal_values 1 2 4 to CYGARC_MAXINT -1024 -20.0 to -10
This indicates three legal values 1
,
2
and -1024
, one
integer range 4 to CYGARC_MAXINT
, and one
floating point range -20.0 to -10.0
. In
practice such list expressions are rarely useful.
The identifier to
is not reserved, so it is
possible to have a configuration option with that name (although it
violates every naming convention). Using that option in a list
expression may however give unexpected results.
The graphical configuration tool uses the legal_values list
expression to determine how best to let users manipulate the option's
value. Different widgets will be appropriate for different lists, so
{ "red" "green" "blue" }
might
involve a pull-down option menu, and
1 to 16
could involve a spinner. The
exact way in which legal_values lists get mapped on to GUI widgets
is not defined and is subject to change at any time.
As with goal expressions, list expressions can be ambiguous. Consider the following hypothetical example:
legal_values CYGNUM_LIBC_RAND_SEED -CYGNUM_LIBC_RAND_TRACE_LEVEL
This could be parsed in two ways:
legal_values (CYGNUM_LIBC_RAND_SEED - CYGNUM_LIBC_RAND_TRACE_LEVEL) legal_values (CYGNUM_LIBC_RAND_SEED) (-CYGNUM_LIBC_RAND_TRACE_LEVEL)
Both are legal. The list expression parsing code will always use the largest ordinary expression for each element, so the first interpretation will be used. In cases like this it is a good idea to use brackets and avoid possible confusion.
3.7. Interfaces
For many configurability requirements, options provide sufficient expressive power. However there are times when a higher level of abstraction is appropriate. 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 future. A first attempt at expressing the dependency might be:
requires CYGSEM_KERNEL_SCHED_MLQUEUE || CYGSEM_KERNEL_SCHED_BITMAP
This constraint will work with the current release, but it is limited. Suppose there is a new release of the kernel which adds another scheduler such as a deadline scheduler, or suppose that there is a new third party package which adds such a scheduler. The package containing the limited constraint would now have to be updated and another release made, with possible knock-on effects.
CDL interfaces provide an abstraction mechanism: constraints can be
expressed in terms of an abstract concept, for example
“scheduler”, rather than specific implementations such as
CYGSEM_KERNEL_SCHED_MLQUEUE
and
CYGSEM_KERNEL_SCHED_BITMAP
. Basically an interface
is a calculated configuration option:
cdl_interface CYGINT_KERNEL_SCHEDULER { display "Number of schedulers in this configuration" … }
The individual schedulers can then implement this interface:
cdl_option CYGSEM_KERNEL_SCHED_MLQUEUE { display "Multi-level queue scheduler" default_value 1 implements CYGINT_KERNEL_SCHEDULER … } cdl_option CYGSEM_KERNEL_SCHED_BITMAP { display "Bitmap scheduler" default_value 0 implements CYGINT_KERNEL_SCHEDULER … }
Future schedulers can also implement this interface. The value of an interface, for the purposes of expression evaluation, is the number of active and enabled options which implement this interface. Packages which rely on the presence of a scheduler can impose constraints such as:
requires CYGINT_KERNEL_SCHEDULER
If none of the schedulers are enabled, or if the kernel package is not
loaded, then CYGINT_KERNEL_SCHEDULER
will evaluate
to 0
. If at least one scheduler is active and
enabled then the constraint will be satisfied.
Because interfaces have a calculated value determined by the
implementors, the default_value and calculated properties are not
applicable and should not appear in the body of a cdl_interface
command. Interfaces have the data
flavor by
default, but the bool
and
booldata
flavors may be specified instead. A
bool
interface is disabled if there are no active
and enabled implementors, otherwise it is enabled. A
booldata
interface is disabled if there are no
active and enabled implementors, otherwise it is enabled and has a
value corresponding to the number of these implementors. Other
properties such as requires and compile can be used as normal.
Some component writers will not want to use interfaces in this way. The reasoning is that their code will only have been tested with the existing schedulers, so the requires constraint needs to be expressed in terms of those schedulers; it is possible that the component will still work with a new scheduler, but there are no guarantees. Other component writers may take a more optimistic view and assume that their code will work with any scheduler until proven otherwise. It is up to individual component writers to decide which approach is most appropriate in any given case.
One common use for interfaces is to describe the hardware
functionality provided by a given target. For example the CDL
scripts for a TCP/IP package might want to know whether or not the
target hardware has an ethernet interface. Generally it is not
necessary for the TCP/IP stack to know exactly which ethernet hardware
is present, since there should be a device driver which implements the
appropriate functionality. In CDL terms the device drivers should
implement an interface CYGHWR_NET_DRIVERS
, and the CDL scripts for the TCP/IP stack can use this in appropriate
expressions.
Note | |
---|---|
Using the term interface for this concept is
sometimes confusing, since the term has various other meanings as
well. In practice, it is often correct. If there is a configuration
option that implements a given CDL interface, then usually this
option will enable some code that provides a particular interface at
the C or C++ level. For example an ethernet device driver implements
the CDL interface |
3.8. Updating the ecos.db database
The current implementation of the component framework requires that all packages be present in a single component repository and listed in that repository's ecos.db database. This is not generally a problem for application developers who can consider the component repository a read-only resource, except when adding or removing packages via the administration tool. However it means that component writers need to do their development work inside a component repository as well, and update the database with details of their new package or packages. Future enhancements to the component framework may allow new components to be developed outside a repository.
Like most files related to the component framework, the ecos.db database is actually a Tcl script. Typical package entries would look like this:
package CYGPKG_LIBC { alias { "C library" libc clib clibrary } directory language/c/libc script libc.cdli attributes { license_ecosgpl } description " This package enables compatibility with the ISO C standard - ISO/IEC 9899:1990. This allows the user application to use well known standard C library functions, and in eCos starts a thread to invoke the user function main()" } package CYGPKG_IO_PCI { alias { "PCI configuration library" io_pci } directory io/pci script io_pci.cdl attributes { license_ecosgpl hardware_io } hardware description " This package contains the PCI configuration library." }
The package
command takes two arguments, a name and
a body. The name must be the same as in the cdl_package
command in
the package's top-level CDL script. The body can contain the
following six commands: alias
,
directory
, script
,
attributes
, hardware
and
description
.
-
alias
Each package should have one or more aliases. The first alias is typically used when listing the known packages, because a string like
C library
is a bit easier to read and understand thanCYGPKG_LIBC
. The other aliases are not used for output, but are accepted on input. For example the ecosconfig command-line tool will acceptadd libc
as an option, as well asadd CYGPKG_LIBC
.-
directory
This is used to specify the location of the package relative to the root of the component repository. It should be noted that in the current component framework this location cannot be changed in subsequent releases of the package: if for some reason it is desirable to install a new release elsewhere in the repository, all the old versions must first be uninstalled; the database cannot hold two separate locations for one package.
-
script
The
script
command specifies the location of the package's top-level CDL script, in other words the one containing thecdl_package
definition. If the package follows the directory layout conventions then this script will be in thecdl
sub-directory, otherwise it will be relative to the package's top-level directory. Again once a release has been made this file should not change in later releases. In practice the top-level script is generally named after the package itself, so changing its name is unlikely to be useful.-
attributes
The
attributes
command provides a set of descriptive names or values which represent attributes of a package. These values are currently used to provide developers with a means to group together packages with common attributes as an aid to package or target selection, or to find or identify packages with certain attributes. For example, the most common use is to identify the licensing under which the source code of a package is distributed to assist with the adherence of the terms of the licenses.-
hardware
Packages which are tied to specific hardware, for example device drivers and HAL packages, should indicate this in both the
cdl_package
command of the CDL script and in the database entry.-
description
This should give a brief description of the package. Typically the text for the description property in the
cdl_package
command will be re-used.
Note | |
---|---|
Most of the information in the ecos.db file could
be obtained by a relatively simple utility. This would be passed a
single argument identifying a package's top-level CDL script. The
directory path relative to the component repository root could be
determined from the filename. The name, |
Currently the ecos.db database also holds information about the various targets. When porting to a new target it will be necessary to add information about the target to the database, as well as the details of the new platform HAL package and any related packages.
[1] It is possible that some versions of the Tcl interpreter will instead produce a result of 54 when asked to multiply six by nine. Appropriate reference documentation should be consulted for more information on why 42 is in fact the correct answer.
2024-12-10 | Open Publication License |