The eCos configuration information is held in a single
savefile, typically ecos.ecc, which can
be generated by either the GUI configuration tool or by the
command line ecosconfig tool. The file
normally exists at the top level of the build tree. It is a
text file, allowing the various configurations options to be
edited inside a suitable text editor or by other programs or
scripts, as well as in the GUI config tool.
An eCos savefile is actually a script in the Tcl programming
language, so any modifications to the file need to preserve Tcl
syntax. For most configuration options, any modifications will be
trivial and there is no need to worry about Tcl syntax. For example,
changing a 1 to a 0 to disable an option. For more complicated
options, for example CYGDAT_UITRON_TASK_EXTERNS,
which involves some lines of C code, more care has
to be taken. If an edited savefile is no longer a valid Tcl script
then the configuration tools will be unable to read back the data
for further processing, for example to generate a build tree. An
outline of Tcl syntax is given below. One point worth noting here
is that a line that begins with a “#” is
usually a comment, and the bulk of an eCos savefile actually consists
of such comments, to make it easier to edit.
An eCos savefile begins with a header, which typically
looks something like this:
# eCos saved configuration
# ---- commands --------------------------------------------------------
# This section contains information about the savefile format.
# It should not be edited. Any modifications made to this section
# may make it impossible for the configuration tools to read
# the savefile.
cdl_savefile_version 1;
cdl_savefile_command cdl_savefile_version {};
cdl_savefile_command cdl_savefile_command {};
cdl_savefile_command
cdl_configuration { description hardware template package };
cdl_savefile_command cdl_package { value_source user_value wizard_value inferred_value };
cdl_savefile_command cdl_component { value_source user_value wizard_value inferred_value };
cdl_savefile_command cdl_option { value_source user_value wizard_value inferred_value };
cdl_savefile_command cdl_interface { value_source user_value wizard_value inferred_value };
|
This section of the savefile is intended for use by the
configuration system, and should not be edited. If this
section is edited then the various configuration tools may no
longer be able to read in the modified savefile.
The header is followed by a section that defines the
configuration as a whole. A typical example would
be:
# ---- toplevel --------------------------------------------------------
# This section defines the toplevel configuration object. The only
# values that can be changed are the name of the configuration and
# the description field. It is not possible to modify the target,
# the template or the set of packages simply by editing the lines
# below because these changes have wide-ranging effects. Instead
# the appropriate tools should be used to make such modifications.
cdl_configuration eCos {
description ““ ;
# These fields should not be modified.
hardware pid ;
template uitron ;
package -hardware CYGPKG_HAL_ARM current ;
package -hardware CYGPKG_HAL_ARM_PID current ;
package -hardware CYGPKG_IO_SERIAL current ;
package -template CYGPKG_HAL current ;
package -template CYGPKG_IO current ;
package -template CYGPKG_INFRA current ;
package -template CYGPKG_KERNEL current ;
package -template CYGPKG_UITRON current ;
package -template CYGPKG_LIBC current ;
package -template CYGPKG_LIBM current ;
package -template CYGPKG_DEVICES_WALLCLOCK current ;
package -template CYGPKG_ERROR current ;
};
|
This section allows the configuration tools to reload the
various packages that make up the configuration. Most of the information
should not be edited. If it is necessary to add a new package or
to remove an existing one then the appropriate tools should be used
for this, for example:
$ ecosconfig remove CYGPKG_LIBM |
There are two fields which can be edited. Configurations have
a name; in this case eCos. They can also have a description, which
is some arbitrary text. The configuration tools do not make use
of these fields, they exist so that users can store additional information
about a configuration.
The toplevel section is followed by details of all the
conflicts (if any) in the configuration, for
example:
# ---- conflicts -------------------------------------------------------
# There are 2 conflicts.
#
# option CYGNUM_LIBC_TIME_DST_DEFAULT_OFFSET
# Property LegalValues
# Illegal current value 100000
# Legal values are: -90000 to 90000
#
# option CYGSEM_LIBC_TIME_CLOCK_WORKING
# Property Requires
# Requires constraint not satisfied: CYGFUN_KERNEL_THREADS_TIMER
|
When editing a configuration you may end up with something
that is invalid. Any problems in the configuration will be reported
in the conflicts section. In this case there are two conflicts.
The option CYGNUM_LIBC_TIME_DST_DEFAULT_OFFSET has
been given an illegal value: typically this would be fixed by searching
for the definition of that option later on in the savefile and modifying
the value. The second conflict is more interesting, an unsatisfied requires constraint.
Configuration options are not independent: disabling some functionality
in, say, the kernel, can have an impact elsewhere; in this case
the C library. The various dependencies between the options are
specified by the component developers and checked by the configuration
system. In this case there are two obvious ways in which the conflict could
be resolved: re-enabling CYGFUN_KERNEL_THREADS_TIMER,
or disabling CYGSEM_LIBC_TIME_CLOCK_WORKING.
Both of these options will be listed later on in the file.
Some care has to be taken when modifying configuration options,
to avoid introducing new conflict. For instance it is possible that
there might be other options in the system which have a dependency
on CYGSEM_LIBC_TIME_CLOCK_WORKING,
so disabling that option may not be the best way to resolve the
conflict. Details of all such dependencies are provided in the appropriate
places in the savefile.
It is not absolutely required that a configuration be conflict-free
before generating a build tree and building eCos. It is up to the
developers of each component to decide what would happen if an attempt
is made to build eCos while there are still conflicts. In serious
cases there is likely to be a compile-time failure, or possibly
a link-time failure. In less serious cases the system may build
happily and the application can be linked with the resulting library,
but the component may not quite function as intended - although
it may still be good enough for the specific needs of the application.
It is also possible that everything builds and links, but once in
a while the system will unaccountably crash. Using a configuration
that still has conflicts is done entirely at the user’s
risk.
The bulk of the savefile lists the various packages,
components, and options, including their values and the
various dependencies. A number of global options come
first, especially those related to the build process such
as compiler flags. These are followed by the various
packages, and the components and options within those
packages, in order.
Packages, components and options are organized in a
hierarchy. If a particular component is disabled then all
options and sub-components below it will be inactive: any
changes made to these will have no effect. The savefile
contains information about the hierarchy in the form of
comments, for example:
cdl_package CYGPKG_KERNEL ...
# >
cdl_component CYGPKG_KERNEL_EXCEPTIONS ...
# >
cdl_option CYGSEM_KERNEL_EXCEPTIONS_DECODE ...
cdl_option CYGSEM_KERNEL_EXCEPTIONS_GLOBAL ...
# <
cdl_component CYGPKG_KERNEL_SCHED ...
# >
cdl_option CYGSEM_KERNEL_SCHED_MLQUEUE ...
cdl_option CYGSEM_KERNEL_SCHED_BITMAP ...
# <
# <
|
This corresponds to the following hierarchy:
CYGPKG_KERNEL
CYGPKG_KERNEL_EXCEPTIONS
CYGSEM_KERNEL_EXCEPTIONS_DECODE
CYGSEM_KERNEL_EXCEPTIONS_GLOBAL
CYGPKG_KERNEL_SCHED
CYGSEM_KERNEL_SCHED_MLQUEUE
CYGSEM_KERNEL_SCHED_BITMAP
|
Providing the hierarchy information in this way allows
programs or scripts to analyze the savefile and readily
determine the hierarchy. It could also be used by a
sufficiently powerful editor to support structured editing
of eCos savefiles. The information is not used by the
configuration tools themselves since they obtain the
hierarchy from the original CDL scripts.
Each configurable entity is preceded by a comment, of
the following form:
# Kernel schedulers
# doc: ref/ecos-ref/ecos-kernel-overview.html#THE-SCHEDULER
# The eCos kernel provides a choice of schedulers. In addition
# there are a number of configuration options to control the
# detailed behaviour of these schedulers.
cdl_component CYGPKG_KERNEL_SCHED {
...
};
|
This provides a short textual alias
Kernel schedulers for the
component. If online documentation is available for the
configurable entity then this will come next. Finally
there is a short description of the entity as a whole. All
this information is provided by the component
developers.
Each configurable entity takes the form:
<type> <name> {
<data>
}; |
Configurable entities may not be active. This can be either
because the parent is disabled or inactive, or because there are
one or more active_if properties. Modifying
the value of an inactive entity has no effect on the configuration,
so this information is provided first:
cdl_option CYGSEM_KERNEL_EXCEPTIONS_DECODE {
# This option is not active
# The parent CYGPKG_KERNEL_EXCEPTIONS is disabled
...
};
...
cdl_option CYGIMP_IDLE_THREAD_YIELD {
# This option is not active
# ActiveIf constraint: (CYGNUM_KERNEL_SCHED_PRIORITIES == 1)
# CYGNUM_KERNEL_SCHED_PRIORITIES == 32
# --> 0
...
};
|
For CYGIMP_IDLE_THREAD_YIELD the
savefile lists the expression that must be satisfied if the option
is to be active, followed by the current value of all entities that
are referenced in the expression, and finally the result of evaluating
that expression.
Not all options are directly modifiable in the savefile. First,
the value of packages (which is the version of that package loaded
into the configuration) cannot be modified here.
cdl_package CYGPKG_KERNEL {
# Packages cannot be added or removed, nor can their version be changed,
# simply by editing their value. Instead the appropriate configuration
# should be used to perform these actions.
...
};
|
The version of a package can be changed using e.g.:
$ ecosconfig version 1.3 CYGPKG_KERNEL |
Even though a package’s value cannot be modified
here, it is still important for the savefile to contain the details.
In particular packages may impose constraints on other configurable
entities and may be referenced by other configurable entities. Also
it would be difficult to understand or extract the configuration’s
hierarchy if the packages were not listed in the appropriate places
in the savefile.
Some components (or, conceivably, options) do not have any
associated data. Typically they serve only to introduce another
level in the hierarchy, which can be useful in the context of the
GUI configuration tool.
cdl_component CYGPKG_HAL_COMMON_INTERRUPTS {
# There is no associated value.
};
|
Other components or options have a calculated value. These
are not user-modifiable, but typically the value will depend on
other options which can be modified. Such calculated options can
be useful when controlling what gets built or what ends up in the
generated configuration header files. A calculated value may also
effect other parts of the configuration, for instance, via a requires constraint.
cdl_option BUFSIZ {
# Calculated value: CYGSEM_LIBC_STDIO_WANT_BUFFERED_IO ? CYGNUM_LIBC_STDIO_BUFSIZE : 0
# CYGSEM_LIBC_STDIO_WANT_BUFFERED_IO == 1
# CYGNUM_LIBC_STDIO_BUFSIZE == 256
# Current_value: 256
};
|
A special type of calculated value is the interface.
The value of an interface is the number of active and enabled options
which implement that interface. Again the value
of an interface cannot be modified directly; only by modifying the
options which implement the interface. However, an interface can
be referenced by other parts of the configuration.
cdl_interface CYGINT_KERNEL_SCHEDULER {
# Implemented by CYGSEM_KERNEL_SCHED_MLQUEUE, active, enabled
# Implemented by CYGSEM_KERNEL_SCHED_BITMAP, active, disabled
# This value cannot be modified here.
# Current_value: 1
# Requires: 1 == CYGINT_KERNEL_SCHEDULER
# CYGINT_KERNEL_SCHEDULER == 1
# --> 1
|
# The following properties are affected by this value
# interface CYGINT_KERNEL_SCHEDULER
# Requires: 1 == CYGINT_KERNEL_SCHEDULER
}; |
If the configurable entity is modifiable then there will be
lines like the following:
cdl_option CYGSEM_KERNEL_SCHED_MLQUEUE {
...
# Flavor: bool
# No user value, uncomment the following line to provide one.
# user_value 1
# value_source default
# Default value: 1
...
};
|
Configurable entities can have one of four different flavors:
none, bool, data and booldata. Flavor none indicates that there
is no data associated with the entity, typically it just acts as
a placeholder in the overall hierarchy. Flavor bool is the most
common, it is a simple yes-or-no choice. Flavor data is for more
complicated configuration choices, for instance the size of an array
or the name of a device. Flavor booldata is a combination of bool
and data: the option can be enabled or disabled, and there is some
additional data associated with the option as well.
In the above example the user has not modified this particular
option, as indicated by the comment and by the commented-out user_value line.
To disable this option the file should be edited to:
cdl_option CYGSEM_KERNEL_SCHED_MLQUEUE {
...
# Flavor: bool
# No user value, uncomment the following line to provide one.
user_value 0
# value_source default
# Default value: 1
...
}
|
The comment preceding the user_value
0 line can be removed if desired, otherwise it
will be removed automatically the next time the file is read and
updated by the configuration tools.
Much the same process should be used for options with the
data flavor, for example:
cdl_option CYGNUM_LIBC_TIME_DST_DEFAULT_OFFSET {
# Flavor: data
# No user value, uncomment the following line to provide one.
# user_value 3600
# value_source default
# Default value: 3600
# Legal values: -90000 to 90000
};
|
can be changed to:
cdl_option CYGNUM_LIBC_TIME_DST_DEFAULT_OFFSET {
# Flavor: data
user_value 7200
# value_source default
# Default value: 3600
# Legal values: -90000 to 90000 };
|
Note that the original text provides the default value in
the user_value comment,
on the assumption that the desired new value is likely to be similar
to the default value. The value_source comment
does not need to be updated, it will be fixed up if the savefile
is fed back into the configuration system and regenerated.
For options with the booldata flavor, the user_value line
needs take two arguments. The first argument is for the boolean
part, the second for the data part. For example:
cdl_component CYGNUM_LIBM_COMPATIBILITY {
# Flavor: booldata
# No user value, uncomment the following line to provide one.
# user_value 1 POSIX
# value_source default
# Default value: 1 POSIX
# Legal values: “POSIX” “IEEE” “XOPEN” “SVID”
...
};
|
could be changed to:
cdl_component CYGNUM_LIBM_COMPATIBILITY {
# Flavor: booldata
user_value 1 IEEE
# value_source default
# Default value: 1 POSIX
# Legal values: “POSIX” “IEEE” “XOPEN” “SVID”
...
};
|
or alternatively, if the whole component should be disabled,
to:
cdl_component CYGNUM_LIBM_COMPATIBILITY {
# Flavor: booldata
user_value 0 POSIX
# value_source default
# Default value: 1 POSIX
# Legal values: “POSIX” “IEEE” “XOPEN” “SVID”
...
};
|
Some options take values that span multiple lines. An example
would be:
cdl_option CYGDAT_UITRON_MEMPOOLVAR_INITIALIZERS {
# Flavor: data
# No user value, uncomment the following line to provide one.
# user_value \
# “CYG_UIT_MEMPOOLVAR( vpool1, 2000 ), \\
# CYG_UIT_MEMPOOLVAR( vpool2, 2000 ), \\
# CYG_UIT_MEMPOOLVAR( vpool3, 2000 ),”
# value_source default
# Default value: \
# “CYG_UIT_MEMPOOLVAR( vpool1, 2000 ), \\
# CYG_UIT_MEMPOOLVAR( vpool2, 2000 ), \\
# CYG_UIT_MEMPOOLVAR( vpool3, 2000 ),”
};
|
Setting a user value for this option involves uncommenting
and modifying all relevant lines, for example:
cdl_option CYGDAT_UITRON_MEMPOOLVAR_INITIALIZERS {
# Flavor: data
user_value \
“CYG_UIT_MEMPOOLVAR( vpool1, 4000 ), \\
CYG_UIT_MEMPOOLVAR( vpool2, 4000 ),”
# value_source default
# Default value: \
# “CYG_UIT_MEMPOOLVAR( vpool1, 2000 ), \\
# CYG_UIT_MEMPOOLVAR( vpool2, 2000 ), \\
# CYG_UIT_MEMPOOLVAR( vpool3, 2000 ),”
};
|
In such cases appropriate care has to be taken to preserve
Tcl syntax, as discussed below.
The configuration system has the ability to keep track of
several different values for any given option. All options
start off with a default value, in other words their value
source is set to default. If a
configuration involves conflicts and the configuration
system’s inference engine is allowed to resolve these
automatically then it may provide an
inferred value instead, for
example:
cdl_option CYGMFN_KERNEL_SYNCH_CONDVAR_TIMED_WAIT {
# Flavor: bool
# No user value, uncomment the following line to provide one.
# user_value 1
# The inferred value should not be edited directly.
inferred_value 0
# value_source inferred
# Default value: 1
...
};
|
Inferred values are calculated by the configuration system
and should not be edited by the user. If the inferred value is not
correct then a user value should be substituted instead:
cdl_option CYGMFN_KERNEL_SYNCH_CONDVAR_TIMED_WAIT {
# Flavor: bool
user_value 1
# The inferred value should not be edited directly.
inferred_value 0
# value_source inferred
# Default value: 1
...
}; |
The inference engine will not override a user value with an
inferred one. Making a change like this may well re-introduce a
conflict, since the inferred value was only calculated to resolve
a conflict. Another run of the inference engine may find a different
and more acceptable way of resolving the conflict, but this is not guaranteed
and it may be up to the user to examine the various dependencies
and work out an acceptable solution.
Inferred values are listed in the savefile because the exact
inferred value may depend on the order in which changes were made
and conflicts were resolved. If the inferred values were absent
then it is possible that reloading a savefile would not exactly
restore the configuration. Default values on the other hand are
entirely deterministic so there is no actual need for the values
to be listed in the savefile. However, the default value can be
very useful information so it is provided in a comment.
Occasionally the user will want to do some experimentation,
and temporarily switch an option from a user value back to a default
or inferred one to see what the effect would be. This could be achieved
by simply commenting out the user value. For instance, if the current
savefile contains:
cdl_option CYGMFN_KERNEL_SYNCH_CONDVAR_TIMED_WAIT {
# Flavor: bool
user_value 1
# The inferred value should not be edited directly.
inferred_value 0
# value_source user
# Default value: 1
...
};
|
then the inferred value could be restored by commenting out
or removing the user_value line:
cdl_option CYGMFN_KERNEL_SYNCH_CONDVAR_TIMED_WAIT {
# Flavor: bool
# user_value 1
# The inferred value should not be edited directly.
inferred_value 0
# value_source user
# Default value: 1
...
};
|
This is fine for simple values. However if the value is complicated
then there is a problem: commenting out the user_value line
or lines means that the user value becomes invisible to the configuration system,
so if the savefile is loaded and then regenerated the information
will be lost. An alternative approach is to keep the user_value but
explicitly set the value_source line,
for example:
cdl_option CYGMFN_KERNEL_SYNCH_CONDVAR_TIMED_WAIT {
# Flavor: bool
user_value 1
# The inferred value should not be edited directly.
inferred_value 0
value_source inferred
# Default value: 1
...
};
|
In this case the configuration system will use the inferred
value for the purposes of dependency analysis etc., even though
a user value is present. To restore the user value the value_source line
can be commented out again. If there is no explicit value_source then
the configuration system will just use the highest priority one:
the user value if it exists; otherwise the inferred value if it
exists; otherwise the default value which always exists.
The default value for an option can be a simple constant,
or it can be an expression involving other options. In the latter
case the expression will be listed, together with the values for
all options referenced in the expression and the current result
of evaluating that expression. This is for informational purposes
only, the default value is always recalculated deterministically
when loading in a savefile.
cdl_option CYGBLD_GLOBAL_COMMAND_PREFIX {
# Flavor: data
# No user value, uncomment the following line to provide one.
# user_value arm-elf
# value_source default
# Default value: CYGHWR_THUMB ? “thumb-elf” : “arm-elf”
# CYGHWR_THUMB == 0
# --> arm-elf
};
|
For options with the data or booldata flavor, there are likely
to be constraints on the possible values. If the value is supposed
to be a number in a given range and a user value of “hello
world” is provided instead then there
are likely to be compile-time failures. Component developers can
specify constraints on the legal values, and these will be listed
in the savefile.
cdl_option X_TLOSS {
# Flavor: data
# No user value, uncomment the following line to provide one.
# user_value 1.41484755040569E+16
# value_source default
# Default value: 1.41484755040569E+16
# Legal values: 1 to 1e308
};
|
cdl_component CYGNUM_LIBM_COMPATIBILITY {
# Flavor: booldata
# No user value, uncomment the following line to provide one.
# user_value 1 POSIX
# value_source default
# Default value: 1 POSIX
# Legal values: “POSIX” “IEEE” “XOPEN” “SVID”
...
};
|
In some cases the legal values list may be an expression involving
other options. If so then the current values of the referenced options
will be listed, together with the result of evaluating the list
expression, just as for default value expressions.
If an illegal value is provided then this will result in a
conflict, listed in the conflicts section of the savefile. For more
complicated options a simple legal values list is not sufficient
to allow the current value to be validated, and the configuration
system will be unable to flag conflicts. This issue will be addressed in
future releases of the configuration system.
Following the value-related fields for a given option, any requires constraints belonging
to this option will be listed. These constraints are only effective
if the option is active and, for bool and booldata flavors, enabled.
If some aspect of eCos functionality is inactive or disabled then
it cannot impose any constraints on the rest of the system. As usual,
the full expression will be listed followed by the current values
of all options that are referenced and the result of evaluating
the expression:
cdl_option CYGSEM_LIBC_TIME_TIME_WORKING {
...
# Requires: CYGPKG_DEVICES_WALLCLOCK
# CYGPKG_DEVICES_WALLCLOCK == current
# --> 1
};
|
When modifying the value of an option it is useful to know
not only what constraints the option imposes on the rest of the
system but also what other options in the system depend in some
way on this one. The savefile provides this information:
cdl_option CYGFUN_KERNEL_THREADS_TIMER {
...
# The following properties are affected by this value
# option CYGMFN_KERNEL_SYNCH_CONDVAR_TIMED_WAIT
# Requires: CYGFUN_KERNEL_THREADS_TIMER
# option CYGIMP_UITRON_STRICT_CONFORMANCE
# Requires: CYGFUN_KERNEL_THREADS_TIMER
# option CYGSEM_LIBC_TIME_CLOCK_WORKING
# Requires: CYGFUN_KERNEL_THREADS_TIMER
};
|
eCos savefiles are implemented as Tcl scripts, and are read
in by running the data through a standard Tcl interpreter that has
been extended with a small number of additional commands such as cdl_option and cdl_configuration.
In many cases this is an implementation detail that can be safely
ignored while editing a savefile: simply replacing a 1 with
a 0 to disable some functionality
is not going to affect whether or not the savefile is still a valid
Tcl script and can be processed by a Tcl interpreter. However, there
are more complicated cases where an understanding of Tcl syntax
is at least desirable, for example:
cdl_option CYGDAT_UITRON_MEMPOOLVAR_EXTERNS {
# Flavor: data
user_value \
“static char vpool1\[ 2000 \], \\
vpool2\[ 2000 \], \\
vpool3\[ 2000 \];”
# value_source default
# Default value: \
# “static char vpool1\[ 2000 \], \\
# vpool2\[ 2000 \], \\
# vpool3\[ 2000 \];”
};
|
The backslash at the end of the user_value line
is processed by the Tcl interpreter as a line continuation character.
The quote marks around the user data are also interpreted by the
Tcl interpreter and serve to turn the entire data field into a single
argument. The backslashes preceding the opening and closing square
brackets prevent the Tcl interpreter from treating these characters
specially, otherwise there would be an attempt at command
substitution as described below. The double backslashes
at the end of each line of the data will be turned into a single
backslash by the Tcl interpreter, rather than escaping the newline
character, so that the actual data seen by the configuration system
is:
static char vpool1[ 2000 ], \
vpool2[ 2000 ], \
vpool3[ 2000 ];
|
This is of course the data that should end up in the µITRON
configuration header file. The opening and closing braces surrounding
the entire body of the option data are also significant and cause
this body to be passed as a single argument to the cdl_option command.
The closing semicolon is optional in this example, but provides
a small amount of additional robustness if the savefile is edited
such that it is no longer a valid Tcl script. If the data contained
any $ characters then
these would have to be treated specially as well, via a backslash escape.
In spite of what all the above might seem to suggest, Tcl
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 behaviour 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:
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:
Any white space surrounding the semicolon is just ignored
because it does not serve to separate arguments.
Now consider the following:
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:
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 eCos savefiles.
For instance, consider the following configuration option:
cdl_option CYGDAT_LIBC_STDIO_DEFAULT_CONSOLE {
# Flavor: data
# No user value, uncomment the following line to provide one.
# user_value “\”/dev/ttydiag\””
# value_source default
# Default value: “\”/dev/ttydiag\””
};
|
The desired value of the configuration option should be a
valid C string, complete with quote characters. If the savefile
was edited to:
cdl_option CYGDAT_LIBC_STDIO_DEFAULT_CONSOLE {
# Flavor: data
user_value “/dev/ttydiag”
# value_source default
# Default value: “\”/dev/ttydiag\””
};
|
then the Tcl interpreter would remove the quote marks when
the savefile is read back in, so the option’s value would
not have any quote marks and would not be a valid C string. The
configuration system is not yet able to perform the required validation
so the following #define would
be generated in the configuration header file:
#define CYGDAT_LIBC_STDIO_DEFAULT_CONSOLE /dev/ttydiag |
This is likely to cause a compile-time failure when building
eCos.
A quoted argument continues until the closing quote character
is encountered, which means that it can span multiple lines. This
can also be encountered in eCos savefiles, for instance, in the CYGDAT_UITRON_MEMPOOLVAR_EXTERNS example
mentioned earlier. Newline or semicolon characters do not terminate
the current command in such cases.
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
“ |
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 54, and then the Tcl interpreter will execute
puts “The answer is 54”. It should be noted that
the interpreter contains 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 line substitution is very unlikely to prove useful
in the context of an eCos savefile, but it is part of the Tcl language
and hence cannot be easily suppressed while reading in a savefile.
As a result care has to be taken when savefile data involves square
brackets. Consider the following:
cdl_option CYGDAT_UITRON_MEMPOOLFIXED_EXTERNS {
...
user_value \
“static char fpool1[ 2000 ],
fpool2[ 2000 ];”
...
}; |
The Tcl interpreter will interpret the square brackets as
an attempt at command substitution and hence it will attempt to
execute the command 2000 with no
arguments. No such command is defined by the Tcl language or by
the savefile-related extensions provided by the configuration system,
so this will result in an error when an attempt is made to read
back the savefile. Instead it is necessary to backslash-escape the
square brackets and thus suppress command substitution:
cdl_option CYGDAT_UITRON_MEMPOOLFIXED_EXTERNS {
...
user_value \
“static char fpool1\[ 2000 \],
fpool2\[ 2000 \];”
...
}; |
Similarly 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 very
unlikely to prove useful in the context of an eCos savefile. Should
it be necessary to have a $ character
in configuration data then again a backslash escape needs to be
used.
cdl_option FOODAT_MONITOR_PROMPT {
...
user_value “\$ “
...
}; |
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).
So, for example, the CYGDAT_UITRON_MEMPOOL_EXTERNFIXED_EXTERNS value
could be written as:
cdl_option CYGDAT_UITRON_MEMPOOLFIXED_EXTERNS {
...
user_value \
{static char fpool1[ 2000 ],
fpool2[ 2000 ];}
...
}; |
The configuration system does not use this when generating
savefiles because for simple edits of a savefile by inexperienced
users the use of brace characters is likely to be a little bit more
confusing than the use of quotes.
At this stage it is worth noting that the basic format of
each configuration option in the savefile makes use of braces:
cdl_option <name> {
...
}; |
The configuration system extends the Tcl language with a small
number of additional commands such as cdl_option.
These commands take two arguments, a name and a body, where the
body consists of all the text between the braces. First a check
is made that the specified option is actually present in the configuration.
Then the body is executed in a recursive invocation of the Tcl interpreter,
this time with additional commands such as user_value and value_source.
If, after editing, the braces are not correctly matched up then
the savefile will no longer be a valid Tcl script and errors will
be reported when the savefile is loaded again.
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:
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 behaviour. Consider
the following:
cdl_option WHATEVER {
# This is a comment }
user_value 42
...
} |
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. Toplevel savefile
processing then resumes, and the next command that is encountered
is user_value. Since the
relevant savefile code 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. Fortunately this
sequence of events is very unlikely to occur when editing generated
savefiles.
This should be sufficient information about Tcl to allow for
safe editing of eCos savefiles. Further information is available
from a wide variety of sources, for example the book Tcl
and the Tk Toolkit by John K Ousterhout.