Editing an eCos Savefile

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.

Header

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.

Toplevel Section

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.

Conflicts Section

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.

Data Section

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 
}; 

Tcl Syntax

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:

puts Hello 
set x 42 

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 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:

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