URL
https://opencores.org/ocsvn/openrisc_me/openrisc_me/trunk
Subversion Repositories openrisc_me
[/] [openrisc/] [trunk/] [rtos/] [ecos-2.0/] [tools/] [src/] [libcdl/] [cdlcore.hxx] - Rev 321
Go to most recent revision | Compare with Previous | Blame | View Log
#ifndef __CDLCORE_HXX
# define __CDLCORE_HXX
//{{{ Banner
//==========================================================================
//
// cdlcore.hxx
//
// The core parts of the library. This header defines aspects of
// CDL that are shared between software cdl, hcdl, scdl, and any
// future languages based on the same core technology.
//
//==========================================================================
//####COPYRIGHTBEGIN####
//
// ----------------------------------------------------------------------------
// Copyright (C) 2002 Bart Veer
// Copyright (C) 1999, 2000, 2001 Red Hat, Inc.
//
// This file is part of the eCos host tools.
//
// This program is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 2 of the License, or (at your option)
// any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//
// ----------------------------------------------------------------------------
//
//####COPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s): bartv
// Contributors: bartv
// Date: 1999-04-15
//
//####DESCRIPTIONEND####
//==========================================================================
//}}}
//{{{ Platform dependencies
// ----------------------------------------------------------------------------
// Visual C++ has the delightful feature that the source browser will generate
// warnings if there are any identifiers of length >= 256 characters, while at
// the same time use of templates in the standard C++ library can easily
// generate functions that long. It appears that the only way to disable the
// warnings is by use of a %$#@(%*&%! #pragma.
//
// Similarly, VC++ gives spurious warnings when it comes to multiple virtual
// inheritance.
#ifdef _MSC_VER
# pragma warning( disable: 4786 )
# pragma warning( disable: 4250 )
#endif
//}}}
//{{{ nested #include's
// ----------------------------------------------------------------------------
// The libcdl API is defined using parts of the standard C++ library,
// including strings and various bits of STL. Therefore these headers must
// be #include'd here for the header file to work.
#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <string>
#include <functional>
#include <algorithm>
// <cctype> is needed in various places in the implementation.
// This #include should be moved to an implementation-specific
// header.
#include <cctype>
// Now for some eCos host-side infrastructure headers.
//
// Get the cyg_int64 data type and CYG_UNUSED_PARAM() macro.
#include <cyg/infra/cyg_type.h>
// Some of the classes need to reference the cyg_assert_class_zeal enum.
// Also inline functions may perform assertions.
#include <cyg/infra/cyg_ass.h>
// This header file also depends on having a suitable Tcl installation
// Unfortunately <tcl.h> does some ugly things in the interests of
// portability, including defining symbols such as EXTERN when
// necessary, and this has to be patched up here as cleanly as possible.
#ifndef CONST
# define __CDL_CONST_UNDEFINED
#endif
#ifndef EXTERN
# define __CDL_EXTERN_UNDEFINED
#endif
#ifndef VOID
# define __CDL_VOID_UNDEFINED
#endif
#ifndef CHAR
# define __CDL_CHAR_UNDEFINED
#endif
#ifndef SHORT
# define __CDL_SHORT_UNDEFINED
#endif
#ifndef LONG
# define __CDL_LONG_UNDEFINED
#endif
extern "C" {
#include <tcl.h>
}
#ifdef __CDL_CONST_UNDEFINED
# undef CONST
# undef __CDL_CONST_UNDEFINED
#endif
#ifdef __CDL_EXTERN_UNDEFINED
# undef EXTERN
# undef __CDL_EXTERN_UNDEFINED
#endif
#ifdef __CDL_VOID_UNDEFINED
# undef VOID
# undef __CDL_VOID_UNDEFINED
#endif
#ifdef __CDL_CHAR_UNDEFINED
# undef CHAR
# undef __CDL_CHAR_UNDEFINED
#endif
#ifdef __CDL_SHORT_UNDEFINED
# undef SHORT
# undef __CDL_SHORT_UNDEFINED
#endif
#ifdef __CDL_LONG_UNDEFINED
# undef LONG
# undef __CDL_LONG_UNDEFINED
#endif
//}}}
//{{{ Primitive types, constants:, enums, etc.
// ----------------------------------------------------------------------------
// The CDL languages are defined in terms of arbitrary precision
// arithmetic. This is necessary to allow e.g. pointers to be
// manipulated at the CDL level on 64 bit target processors.
//
// Temporarily it is not necessary to provide this precision, so it is
// convenient to stick to 64 bit integers as provided by the
// underlying infrastructure. However the API is defined in terms of
// the type cdl_int, so that it will be easier in future to make the
// change to the correct datatype. At that point cdl_int can be
// redefined to be a class which supports the appropriate operators.
typedef cyg_int64 cdl_int;
// ---------------------------------------------------------------------------
// A common concept in the CDL language is a small amount of TCL code.
// This is currently stored as a simple string. Conceivably it could
// be byte-compiled and stored accordingly.
typedef std::string cdl_tcl_code;
// ----------------------------------------------------------------------------
// CDL values.
//
// CDL is a declarative programming language. It does involve the
// manipulation of values, but such values do not necessarily
// correspond to hardware-level entities such as integers or double
// precision numbers. Hence the term "type" is avoided, "flavor"
// is used instead. CDL understands four different flavors.
//
// None | Bool
// --------+--------
// Data |BoolData
//
//
// The flavor "none" is used for entities that serve only as
// placeholders in the hierarchy, allowing other entities to be
// grouped more easily.
//
// Boolean entities can be either enabled or disabled. This is the
// most common flavor for software configuration options, the user can
// either enable or disable some unit of functionality. For software
// packages implemented in C or C++ the implementation is obvious: iff
// the entity is enabled then there will be a #define, and code will
// check the setting using e.g. #ifdef.
//
// The flavor "data" implies some arbitrary data. Internally this will
// be held as a string. Other properties such as legal_values,
// check_proc and entry_proc can be used to constrain the
// actual values, for example to an integer value within a certain
// range.
//
// The flavor "booldata" combines the previous two: it means that
// the option can be either enabled or disabled, and if it is
// enabled then it must have a value as per legal_values etc.
// One example of this is a software package: this may be either
// enabled or disabled, and if it is enabled then it has a value
// corresponding to the version string. Another example is a hardware
// pin: this may or may not be connected, and if it is connected
// then its value identifies some other pin.
//
// An entity's flavor is not always sufficient by itself to specify
// how the user can manipulate it in a graphical tool. Obviously an
// entity of flavor "none" cannot be manipulated at all. Flavor "bool"
// normally implies a checkbutton, but occasionally a radiobutton will
// be more appropriate. "Data" says very little about the user
// interaction, it will be necessary to examine other properties such
// as legal_values to determine a sensible representation. The same
// goes for "BoolData", with the additional possibility that the
// entity may be disabled.
//
// It can be argued that three of the flavors are redundant: both Bool
// and BoolData could be implemented as cases of "Data" with a special
// legal value "disabled" (or false, or whatever); "None" could be
// implemented as constant "Data"; effectively CDL would manipulate
// all data as strings, just like e.g. all variables in Tcl, or just
// like all scalars in Perl. This approach is certainly tempting and
// might well make it easier to document the language, but in practice
// it would result in more verbose CDL: boolean entities really are a
// different beast from data entities.
//
// It can also be argued that there should be more flavors. For
// example there could be separate flavors for integer data, floating
// point data, string data, and so on. There are a number of good
// reasons for not doing so:
//
// 1) applying separate constraints such as legal_values allows much
// finer control over the actual values, for example numbers within a
// given range. As likely as not, a value will be constrained to
// something smaller than the range MININT to MAXINT (whatever those
// happen to be for the current target).
//
// 2) where do you stop? Do you provide separate flavors for signed
// vs. unsigned? Char, wchar_t, short, int, long, long long? How about
// the eCos data types cyg_ucount8, cyg_uint8, ... Is there support
// for enums? Arrays? Bitfields? Structures? Unions? C++ classes?
// How about other programming languages such as Ada or Java?
//
// Any attempt to implement a grand union of all data types in CDL
// is doomed to failure and should not be attempted. Treating
// everything as a string instead has proven successful in a number
// of languages, including Tcl and Perl.
//
// 3) for some variants of CDL, for example hardware CDL, it may not
// make much sense to display a value directly and allow it to be
// manipulated directly. The value associated with a pin entity
// identifies the pin to which it is connected, and typically
// this value will be manipulated by drag and drop rather than by
// typing some characters. Such a value certainly does not correspond
// to any machine data type.
//
// Another reason for extending the number of flavors is to provide
// more information. For example there could be a specialized version
// of the boolean flavor called "radio". This would imply a specific
// representation in the user interface, and it would also impose
// a constraint that it implicitly precludes any other radio entities
// within the same group. However the same information can be specified
// by other more general means such as requires statements.
enum CdlValueFlavor {
CdlValueFlavor_Invalid = 0,
CdlValueFlavor_None = 1,
CdlValueFlavor_Bool = 2,
CdlValueFlavor_BoolData = 3,
CdlValueFlavor_Data = 4
};
// Another important aspect of a value is where it came from. There
// are a number of possible sources: the default value, calculated
// from a default_value property; a value inferred by the inference
// engine; a value set by a wizard; and a value set explicitly by
// the user. These sources have different priorities, so for example
// the inference engine can safely replace a calculated default
// value without prompting the user, but changing a user-set value
// automatically is undesirable.
//
// Wizard-generated values are considered more valuable than default
// or inferred values (there is some user input involved), but less
// valuable than values set explicitly by the user: the idea is that
// a wizard asks fairly generic questions and makes a best guess at
// the correct values, which may not be precise enough for the
// user's needs.
//
// Arguably dialogs provide a level between wizards and users, in that
// a dialog can theoretically manipulate several entities in one go so
// it is a less precise way of setting values. At this stage it does
// not seem worthwhile to add this distinction.
//
// The library actually maintains separate values for each source,
// as well as the current source which is what actually gets used.
// In theory it is possible for the user interface code to let
// the user switch between these. It is not yet clear whether this
// makes sense from an end user's perspective.
enum CdlValueSource {
CdlValueSource_Invalid = -1, // 0 is needed for array indexing
CdlValueSource_Default = 0,
CdlValueSource_Inferred = 1,
CdlValueSource_Wizard = 2,
CdlValueSource_User = 3,
CdlValueSource_Current = 4
};
// ----------------------------------------------------------------------------
// Update support.
//
// When there is a change to a node and there are references to that node,
// the referencing properties will want to be informed about this. There
// are various different kinds of changes, not all of which are always
// relevant. For example, if a CDL entity gets destroyed or unloaded then
// all referencing entities are likely to want to know about this, but
// if a container's value changes then this has no effect on a reference
// in e.g. a "parent" property. In some cases it is also useful to apply
// updates to nodes rather than properties, e.g. when a node becomes
// active or inactive.
//
// The generic update code is also used for initialization and finalization,
// i.e. when the source object itself has just been loaded or is
// being unloaded.
//
// For any particular update at most one bit set, but it is often
// appropriate to treat several different kinds of update with
// common code. Hence the enum values can be or'ed and and'ed.
enum CdlUpdate {
CdlUpdate_Loaded = 0x0001, // The source has just been loaded
CdlUpdate_Init = 0x0002, // Second-phase of a load operation
CdlUpdate_Unloading = 0x0004, // The source is being unloaded
CdlUpdate_Created = 0x0008, // The destination has just been created
CdlUpdate_Destroyed = 0x0010, // The destination is being destroyed
CdlUpdate_ValueChange = 0x0020, // The destination's value has changed.
// This gets applied to nodes as well
CdlUpdate_ActiveChange = 0x0040 // The node has become active or inactive
};
// ----------------------------------------------------------------------------
// Inference engine callback.
//
// During a transaction there may be one or more invocations of the inference
// engine, followed by a callback which should display the current transaction
// status to the user and allow one or more recommended fixes to be accepted.
// The callback's return code indicates what should happen next. "Cancel"
// is pretty obvious. "Continue" may result in a commit, or it may result in
// another iteration.
enum CdlInferenceCallbackResult {
CdlInferenceCallbackResult_Continue = 0x01,
CdlInferenceCallbackResult_Cancel = 0x02
};
// ----------------------------------------------------------------------------
// Widget hints.
//
// The library can provide a hint to the GUI code as to a sensible
// widget to use for displaying a particular valuable. There are separate
// hints for the bool and data parts.
enum CdlBoolWidget {
CdlBoolWidget_None = 0, // The boolean part is not applicable
CdlBoolWidget_CustomDialog = 1, // There is a valid custom dialog property
CdlBoolWidget_CheckButton = 2, // For simple booleans
CdlBoolWidget_Radio = 3, // For several mutual exclusive options,
// the data structure will provide a string identifier
};
enum CdlValueWidget {
CdlValueWidget_None = 0, // The value part is not applicable
CdlValueWidget_CustomDialog = 1, // There is a valid custom dialog property
CdlValueWidget_Loadable = 2, // Use package/version dialog
CdlValueWidget_EntryBox = 3, // Fallback
CdlValueWidget_MultilineString = 4, // For complicated strings
CdlValueWidget_DecimalRange = 5, // e.g. 1 to 16
// Could be implemented as scale, radio buttons, entry, pull-down menu,
// combo box, ... depending on GUI conventions and number of entries
CdlValueWidget_HexRange = 6, // e.g. 0x01 to 0x10
CdlValueWidget_OctalRange = 7, // e.g. 01 to 020
CdlValueWidget_DoubleRange = 8, // e.g. 0.1 to 0.2
CdlValueWidget_NumericSet = 9, // e.g. 1 2 4 8 16
// The exact nature of the numbers is irrelevant, they will only
// get displayed, not edited
// Could be implemented as radio buttons, entry widget, pull-down menu,
// combo box, ... depending on GUI conventions and number of entries
// Each entry can have its own representation
CdlValueWidget_StringSet = 10 // e.g. "ram", "rom"
// More to be added, e.g. for compiler flag handling
};
// ----------------------------------------------------------------------------
// Value formats.
//
// The CDL input data can accept numbers in a variety of formats,
// for example hexadecimal as well as decimal. It is desirable to try
// to keep track of this formatting information where possible, so
// that what the user sees and what ends up in header files corresponds
// more closely to what is in the raw CDL data. For example, it is
// much easier to understand 0x7fffffff than its decimal equivalent.
//
// The information kept here is very imprecise, it provides only
// minimal formatting information. It is not clear yet whether this
// will suffice or whether something more exact is going to be needed.
enum CdlValueFormat
{
CdlValueFormat_Default = 0,
CdlValueFormat_Hex = 1,
CdlValueFormat_Octal = 2
};
//}}}
//{{{ Exception classes
// ----------------------------------------------------------------------------
// Some parts of the library make use of C++ exception handling. A number
// of exception classes related to this library are useful. In addition
// just about every part of the library can throw std::bad_alloc, but this
// is not checked for explicitly anywhere.
// This class is used for all exceptions where an error message should
// be displayed to the user. There is a single string message associated
// with the exception.
class CdlStringException {
friend class CdlTest;
public:
CdlStringException(std::string message_arg) {
message = message_arg;
}
CdlStringException(const CdlStringException& original) {
message = original.message;
}
CdlStringException& operator=(const CdlStringException& original) {
message = original.message;
return *this;
}
~CdlStringException() {
message = "";
}
const std::string& get_message() const {
return message;
}
private:
std::string message;
CdlStringException();
};
// CdlInputOutputException: this gets thrown when something goes wrong during
// file I/O operations, e.g. a file exists but cannot be opened. The
// exception contains a simple string explaining the error. This string
// may contain multiple lines, it is intended to be written to stderr
// or displayed in either a text widget or a dialog box.
//
// A separate class rather than a typedef is used to avoid any possible
// error message confusion. Everything gets inlined so there should be
// no performance issues.
class CdlInputOutputException : public CdlStringException {
friend class CdlTest;
public:
CdlInputOutputException(std::string message_arg) :
CdlStringException(message_arg) {
}
CdlInputOutputException(const CdlInputOutputException& original) :
CdlStringException(original) {
}
CdlInputOutputException& operator=(const CdlInputOutputException& original) {
(void) CdlStringException::operator=(original);
return *this;
}
};
// This class is used when any parsing happens at the C++ level rather
// than at the Tcl level. The exception should be caught before it
// propagates through the Tcl interpreter, or the latter will end up
// in an inconsistent state.
class CdlParseException : public CdlStringException {
friend class CdlTest;
public:
CdlParseException(std::string message_arg) :
CdlStringException(message_arg) {
}
CdlParseException(const CdlParseException& original) :
CdlStringException(original) {
}
CdlParseException& operator=(const CdlParseException& original) {
(void) CdlStringException::operator=(original);
return *this;
}
};
// Evaluating an expression may fail for a variety of reasons, e.g. because
// some referenced entity has not been loaded into the configuration.
// This exception can be thrown in such cases.
class CdlEvalException : public CdlStringException {
friend class CdlTest;
public:
CdlEvalException(std::string message_arg) :
CdlStringException(message_arg) {
}
CdlEvalException(const CdlEvalException& original) :
CdlStringException(original) {
}
CdlEvalException& operator=(const CdlEvalException& original) {
(void) CdlStringException::operator=(original);
return *this;
}
};
//}}}
//{{{ Forward declarations of the body classes
// ----------------------------------------------------------------------------
// This section provides forward declarations of the main classes in
// the core of the library. Each variant of CDL will define additional
// classes, e.g. cdl_option, but these will usually be derived from
// the core ones.
// There are three types of expression in CDL:
// 1) ordinary expressions evaluate to a single value. The most common
// use is for the legal_values property.
// 2) list expressions evaluate to a range of values, e.g. 1 to 10,
// and the most common use is for the legal_values property.
// 3) goal expressions evaluate to either true or false and are used
// for e.g. requires and active_if properties.
class CdlExpressionBody;
class CdlListExpressionBody;
class CdlGoalExpressionBody;
// There are also objects for simple values, values and list values.
// These are expanded classes, there are no associated pointer
// types. It is quite likely that values need to be copied around
// on the stack.
class CdlSimpleValue;
class CdlValue;
class CdlListValue;
// Properties. The base class is CdlProperty, and there are a number
// of derived classes provided as standard. Additional derived classes
// may be added in future.
class CdlPropertyBody;
class CdlProperty_MinimalBody;
class CdlProperty_StringBody;
class CdlProperty_TclCodeBody;
class CdlProperty_ReferenceBody;
class CdlProperty_StringVectorBody;
class CdlProperty_ExpressionBody;
class CdlProperty_ListExpressionBody;
class CdlProperty_GoalExpressionBody;
// Base classes. CDL entities such as options and components derive
// from one or more of these, using virtual inheritance.
//
// The lowest-level class is CdlNodeBody.
//
// 1) a node usually lives in a hierarchy, below a toplevel
// and with a container object as the parent. However nodes
// can live outside a container on a temporary basis,
// and toplevel objects have no parent.
//
// 2) a node has a name that is unique within the hierarchy.
//
// 3) a node has a vector of properties. Actually some entities
// will have an empty vector, e.g. the orphans container
// that is internal to the library. However it is too
// inconvenient to have separate base classes for these.
//
// 4) nodes can be referred to by properties in other nodes.
class CdlNodeBody;
// A container is a node that can contain other nodes.
class CdlContainerBody;
// A loadable object is a container whose data has come out of a CDL
// script of some sort. It also stores details about all entities that
// were loaded via this script (even if some of them were reparented)
// thus supporting unload operations.
class CdlLoadableBody;
// A toplevel object is a container that acts as the toplevel of
// a hierarchy, in other words its parent is always 0. In addition
// a toplevel keeps track of all the names used in the hierarchy,
// thus facilitating navigation.
class CdlToplevelBody;
// The remaining classes all add functionality to CdlNode, directly or
// indirectly.
//
// A user-visible object is likely to appear in the user interface.
// This means it may have an alias string, a description, a
// documentation URL, and a gui_hint field.
class CdlUserVisibleBody;
// A valuable object has a value that can be retrieved but not
// necessarily modified by the user. For example the value of an
// interface is always calculated and users can never change it.
// Valuable objects have a whole bunch of associated properties
// including dependencies.
class CdlValuableBody;
// A parentable object has the parent property, i.e. it can
// be reparented to anywhere in the hierarchy
class CdlParentableBody;
// A buildable object is a valuable object that may result in
// something being built, typically a library in the case of
// software packages.
class CdlBuildableBody;
// A loadable that contains buildables
class CdlBuildLoadableBody;
// A definable object is a valuable object whose value can result
// in #define statements in a header file
class CdlDefinableBody;
// A loadable which can contain definables
class CdlDefineLoadableBody;
// TODO: add instantiation support
// Custom dialogs and wizards are provided by the core.
class CdlDialogBody;
class CdlWizardBody;
class CdlInterfaceBody;
// Support for Tcl interpreters is also in the core, since it is
// difficult to do anything CDL-related without at least one Tcl
// interpreter lying around.
class CdlInterpreterBody;
// The basic conflict class is part of the core library, as are a
// number of common derived classes for specific types of conflict.
class CdlConflictBody;
class CdlConflict_UnresolvedBody;
class CdlConflict_IllegalValueBody;
class CdlConflict_EvalExceptionBody;
class CdlConflict_RequiresBody;
class CdlConflict_DataBody;
// Many operations happen (or may happen) in the context of a
// transaction. This is necessary to keep track of the various
// changes that can happen: for example, changing a component's
// value may require other entities' default values to be
// recalculated; it may change some legal_values list expressions,
// causing current values to become invalid; it may affect
// "requires" properties, causing goals to become satisfied or
// not-satisfied; it may change the "active" state of everything
// below the component, not to mention any entity with an
// "active_if" properties, and when an entity becomes active or
// inactive that may in turn affect other entities.
//
// Keeping track of all of this via recursion is possible, but there
// are problems. If an entity is updated multiple times, no
// optimizations are possible. It becomes much more difficult to
// detect cycles. During an unload operation things can get very
// messy. There is no easy way to track all of the changes and report
// them to higher level code via a callback. There is no support
// for any kind of rollback. A transaction model potentially
// provides support for all of this, at the cost of a more
// complex API.
class CdlTransactionBody;
// This class is used to pass information back to the application
// about what has actually changed in a transaction.
class CdlTransactionCallback;
// Build info class. This is always an expanded object, but is
// needed here to break a circular dependency.
class CdlBuildInfo;
// ----------------------------------------------------------------------------
// Typedefs for the pointers. There are separate typedefs to cope with
// const vs. non-const objects. Otherwise you end up with the problem
// that "const CdlNode x" means that the pointer is const, not the
// object pointed at.
typedef CdlExpressionBody* CdlExpression;
typedef CdlListExpressionBody* CdlListExpression;
typedef CdlGoalExpressionBody* CdlGoalExpression;
typedef CdlPropertyBody* CdlProperty;
typedef CdlProperty_MinimalBody* CdlProperty_Minimal;
typedef CdlProperty_StringBody* CdlProperty_String;
typedef CdlProperty_TclCodeBody* CdlProperty_TclCode;
typedef CdlProperty_ReferenceBody* CdlProperty_Reference;
typedef CdlProperty_StringVectorBody* CdlProperty_StringVector;
typedef CdlProperty_ExpressionBody* CdlProperty_Expression;
typedef CdlProperty_ListExpressionBody* CdlProperty_ListExpression;
typedef CdlProperty_GoalExpressionBody* CdlProperty_GoalExpression;
typedef CdlNodeBody* CdlNode;
typedef CdlContainerBody* CdlContainer;
typedef CdlLoadableBody* CdlLoadable;
typedef CdlToplevelBody* CdlToplevel;
typedef CdlUserVisibleBody* CdlUserVisible;
typedef CdlValuableBody* CdlValuable;
typedef CdlParentableBody* CdlParentable;
typedef CdlBuildableBody* CdlBuildable;
typedef CdlBuildLoadableBody* CdlBuildLoadable;
typedef CdlDefinableBody* CdlDefinable;
typedef CdlDefineLoadableBody* CdlDefineLoadable;
typedef CdlDialogBody* CdlDialog;
typedef CdlWizardBody* CdlWizard;
typedef CdlInterfaceBody* CdlInterface;
typedef CdlInterpreterBody* CdlInterpreter;
typedef CdlConflictBody* CdlConflict;
typedef CdlConflict_UnresolvedBody* CdlConflict_Unresolved;
typedef CdlConflict_IllegalValueBody* CdlConflict_IllegalValue;
typedef CdlConflict_EvalExceptionBody* CdlConflict_EvalException;
typedef CdlConflict_RequiresBody* CdlConflict_Requires;
typedef CdlConflict_DataBody* CdlConflict_Data;
typedef CdlTransactionBody* CdlTransaction;
// ----------------------------------------------------------------------------
typedef const CdlExpressionBody* CdlConstExpression;
typedef const CdlListExpressionBody* CdlConstListExpression;
typedef const CdlGoalExpressionBody* CdlConstGoalExpression;
typedef const CdlPropertyBody* CdlConstProperty;
typedef const CdlProperty_MinimalBody* CdlConstProperty_Minimal;
typedef const CdlProperty_StringBody* CdlConstProperty_String;
typedef const CdlProperty_TclCodeBody* CdlConstProperty_TclCode;
typedef const CdlProperty_ReferenceBody* CdlConstProperty_Reference;
typedef const CdlProperty_StringVectorBody* CdlConstProperty_StringVector;
typedef const CdlProperty_ExpressionBody* CdlConstProperty_Expression;
typedef const CdlProperty_ListExpressionBody* CdlConstProperty_ListExpression;
typedef const CdlProperty_GoalExpressionBody* CdlConstProperty_GoalExpression;
typedef const CdlNodeBody* CdlConstNode;
typedef const CdlContainerBody* CdlConstContainer;
typedef const CdlLoadableBody* CdlConstLoadable;
typedef const CdlToplevelBody* CdlConstToplevel;
typedef const CdlUserVisibleBody* CdlConstUserVisible;
typedef const CdlValuableBody* CdlConstValuable;
typedef const CdlParentableBody* CdlConstParentable;
typedef const CdlBuildableBody* CdlConstBuildable;
typedef const CdlBuildLoadableBody* CdlConstBuildLoadable;
typedef const CdlDefinableBody* CdlConstDefinable;
typedef const CdlDefineLoadableBody* CdlConstDefineLoadable;
typedef const CdlDialogBody* CdlConstDialog;
typedef const CdlWizardBody* CdlConstWizard;
typedef const CdlInterfaceBody* CdlConstInterface;
typedef const CdlInterpreterBody* CdlConstInterpreter;
typedef const CdlConflictBody* CdlConstConflict;
typedef const CdlConflict_UnresolvedBody* CdlConstConflict_Unresolved;
typedef const CdlConflict_IllegalValueBody* CdlConstConflict_IllegalValue;
typedef const CdlConflict_EvalExceptionBody* CdlConstConflict_EvalException;
typedef const CdlConflict_RequiresBody* CdlConstConflict_Requires;
typedef const CdlConflict_DataBody* CdlConstConflict_Data;
typedef const CdlTransactionBody* CdlConstTransaction;
//}}}
//{{{ Miscellaneous types etc.
// ----------------------------------------------------------------------------
// This section is used for data types, function prototypes, etc. which could
// not be defined until after the main CDL classes and handles.
// This typedef is used for error and warning reporting functions.
// Typically such a function pointer will be passed when the library
// is asked to perform any non-trivial parsing operation, e.g. loading
// a package.
//
// If the error is fatal then this callback function should raise
// a CdlParseException.
typedef void (*CdlDiagnosticFnPtr)(std::string);
// ----------------------------------------------------------------------------
// This function is used for update handler. Whenever there is a change
// to CDL entity (it has just been loaded, or its value has changed, or
// whatever) this can affect other CDL entities that reference it.
// All such references occur via properties, and there should be
// update handlers associated with those properties.
//
// Update handlers are also invoked for initialization and finalization
// operations, i.e. when the source object itself has just been loaded
// or is in the process of being unloaded.
//
// The arguments to an update handler are:
// 1) the transaction in which the operation takes place
// 2) the source object containing the reference
// 3) the source property containing the reference
// 4) the destination object. This may be 0 for some update
// operations.
// 5) an indication of the change that has happened. This should
// be a CdlUpdate value.
typedef void (*CdlUpdateHandler)(CdlTransaction, CdlNode, CdlProperty, CdlNode, CdlUpdate);
// ----------------------------------------------------------------------------
// This function is also used for transactions. Typically during a
// transaction there will be one or more invocations of the inference engine,
// with callbacks in between to allow one or more of the recommended
// changes to be undone.
typedef CdlInferenceCallbackResult (*CdlInferenceCallback)(CdlTransaction);
// ----------------------------------------------------------------------------
// The TCL API and C++ do not always mesh cleanly, for example a lot
// happens in terms of ClientData which is a void* pointer. To avoid
// too many casts all over the place libcdl provides a CdlInterpreter
// class and the following alternative to Tcl_CmdProc*. A single
// function will be used for the TCL command: its ClientData will be
// the CdlInterpreterCommand, and the CdlInterpreter is accessible via
// AssocData. This does result in some overheads, but none of these
// should be in performance-critical code.
typedef int (*CdlInterpreterCommand)(CdlInterpreter, int, const char*[]);
// ----------------------------------------------------------------------------
// In the libcdl world it is often convenient to swap whole sets of
// commands in and out. For example when executing the body of a
// cdl_component it is desirable to swap in commands for all the
// properties that make sense in a component and swap out all the
// commands that made sense in a higher level. It is assumed that none
// of the commands being swapped in or out are built-ins. Achieving
// this involves a vector of this simple utility structure.
class CdlInterpreterCommandEntry {
public:
std::string name;
CdlInterpreterCommand command;
CdlInterpreterCommandEntry() : name(""), command(0) {}
CdlInterpreterCommandEntry(const char *name_arg, CdlInterpreterCommand command_arg)
: name(name_arg), command(command_arg)
{
}
CdlInterpreterCommandEntry(std::string name_arg, CdlInterpreterCommand command_arg)
: name(name_arg), command(command_arg)
{
}
~CdlInterpreterCommandEntry()
{
name = "";
command = 0;
}
};
// ----------------------------------------------------------------------------
// Persistence support.
// Some applications want to be able to store additional information
// in savefiles, and essentially this involves extra commands that
// get executed when the savefile is executed. It is possible that
// the application reading back the savefile does not understand
// the same set of commands as the application that wrote back the
// data, so the library tries hard not to lose data.
//
// The CdlSaveCallback function typedef is used when installing
// an application-specific savefile command. The first argument
// indicates the node for which the callback is being invoked:
// this may be the entire toplevel, or just an option, or whatever.
//
// The CdlSavefileCommand structure keeps track of the command,
// the save callback if any (non-zero only for application-specific
// data, zero implies that the command is handled by the lirary).
// The load command is invoked when reading in a savefile and the
// appropriate command is executed: unrecognised commands will be
// processed by CdlToplevelBody::savefile_handle_unknown().
typedef void (*CdlSaveCallback)(CdlNode, CdlInterpreter, Tcl_Channel, int);
struct CdlSavefileCommand {
std::string name;
CdlSaveCallback save_callback;
CdlInterpreterCommand load_command;
};
// ----------------------------------------------------------------------------
// Widget hint.
// This structure provides widget hint information for a CdlValuable.
// There are separate hints for the bool and data parts, and possibly
// some additional data such as a string identifying the set of
// items in a radio button.
struct CdlWidgetHint {
CdlBoolWidget bool_widget;
CdlValueWidget value_widget;
std::string radio_button_interface;
};
//}}}
//{{{ Memory leak detection
// ----------------------------------------------------------------------------
// Provide some macros that are useful for detecting memory leaks. Basically
// there is a static counter for every class, which gets incremented by the
// constructor(s) and decremented by the destructor. Memory leak detection
// is currently enabled if tracing is enabled. It would be possible to use
// another configure-time option, but the overheads of tracing are likely
// to dwarf the overheads of memory leak detection.
//
// For now the memleak counters are always present, even in non-debug
// versions. The overhead is sufficiently small that it can be
// ignored.There is control over whether or not the counters get
// updated in the constructor or destructor. Otherwise there would be problems
// with whether or not there should be a semicolon at the end of the
// CYGDBG_DECLARE_MEMLEAK_COUNTER() macro definition.
#define CYGDBG_DECLARE_MEMLEAK_COUNTER() static int memleak_counter
#define CYGDBG_DEFINE_MEMLEAK_COUNTER(class) int class::memleak_counter = 0
#define CYGDBG_GET_MEMLEAK_COUNTER(class) class::memleak_counter
#ifdef CYGDBG_USE_TRACING
#define CYGDBG_MEMLEAK_CONSTRUCTOR() this->memleak_counter++;
#define CYGDBG_MEMLEAK_DESTRUCTOR() this->memleak_counter--;
#define CYGDBG_MEMLEAK_CHECKTHIS() if (this->memleak_counter < 0) { return false; }
#else
#define CYGDBG_MEMLEAK_CONSTRUCTOR()
#define CYGDBG_MEMLEAK_DESTRUCTOR()
#define CYGDBG_MEMLEAK_CHECKTHIS()
#endif
//}}}
//{{{ Cdl class
// ---------------------------------------------------------------------------
// The sole purpose of this class is to provide some utility functions with
// reasonable namespace protection, without requiring that the compiler
// implements namespaces.
class Cdl {
public:
static bool is_valid_value_flavor(CdlValueFlavor);
static bool is_valid_value_source(CdlValueSource);
static bool is_valid_cdl_name(const std::string&);
static bool is_valid_c_preprocessor_symbol(const std::string&);
static bool string_to_integer(std::string, cdl_int&);
static bool string_to_double(std::string, double&);
static bool string_to_bool(std::string, bool&);
static void integer_to_string(cdl_int, std::string&, CdlValueFormat = CdlValueFormat_Default);
static std::string integer_to_string(cdl_int, CdlValueFormat = CdlValueFormat_Default);
static void double_to_string(double, std::string&, CdlValueFormat = CdlValueFormat_Default);
static std::string double_to_string(double, CdlValueFormat = CdlValueFormat_Default);
static void bool_to_string(bool, std::string&);
static std::string bool_to_string(bool);
static void integer_to_double(cdl_int, double&);
static double integer_to_double(cdl_int);
static bool double_to_integer(double, cdl_int&);
static bool string_to_flavor(std::string, CdlValueFlavor&);
static bool flavor_to_string(CdlValueFlavor, std::string&);
static bool string_to_source(std::string, CdlValueSource&);
static bool source_to_string(CdlValueSource, std::string&);
static std::string get_library_version();
static void set_interactive(bool = true);
static bool is_interactive();
static bool truth() { return true; }
static bool falsehood() { return false; }
// return values are -1,0,1 just like strcmp(). The most recent
// version is the smallest.
static int compare_versions(std::string, std::string);
// Also provide an STL-friendly comparison class
class version_cmp {
public:
bool operator()(const std::string& v1, const std::string& v2) const {
return Cdl::compare_versions(v1,v2) < 0;
}
};
// Split a version string into major, minor and release numbers.
static void split_version_string(const std::string&, std::string& /* major */,
std::string& /* minor */, std::string& /* release */);
// It is occasionally useful to take a full CDL name such as CYGPKG_KERNEL
// and turn it into a short form, i.e. kernel.
static std::string get_short_form(const std::string&);
private:
static bool interactive;
};
//}}}
//{{{ CdlInterpreter class
// ----------------------------------------------------------------------------
// libcdl requires access to a Tcl interpreter. For now the standard
// interpreter is used. In the long run it may be better to use a
// custom parser in places, if only to improve the diagnostics messages
// that users see.
//
// Consider the case of software CDL (other CDL variants will have
// similar requirements). A Tcl interpreter is needed to read in the
// data for a given package. It will also be needed at various stages
// when the data is being manipulated, e.g. to display a custom dialog
// or to execute e.g. a check_proc or a define_proc. Each package
// should run in its own safe interpreter with limited capabilities:
// file I/O is limited to read-only, but read-write in the build and
// install trees; network I/O is out of the question, at least until
// appropriate security support is added to the CDL language itself.
// However the interpreter should be extended with additional commands
// like cdl_get and cdl_set to access the configuration data.
//
// For security and robustness reasons it is desirable to have
// separate interpreters for the various packages. This leads to the
// concept of a master interpreter for the entire configuration, and a
// group of slave interpreters, one per package. In this model it
// is convenient to have the configuration and package entities
// associated directly with the interpreter. Note that a single
// application may have several configurations loaded in memory,
// so there may be several master interpreters.
//
// Some applications will want to support the graphical side of CDL,
// i.e. custom dialogs and wizards. This means linking in Tk, not to
// mention X11 (or the Windows equivalents), and making some/all of
// the Tk commands available to the safe interpreter. Arguably
// commands like toplevel should always be disabled. Not all clients
// of libcdl will want the overheads of linking with Tk and X, so this
// has to be made optional.
//
// The approach taken is as follows:
//
// 1) there is a class CdlInterpreter which provides access to Tcl
// interpreters. Amongst other things it takes care of converting
// between C and C++ strings.
//
// 2) every toplevel needs its own CdlInterpreter. The application
// code should supply this interpreter itself when the toplevel
// is instantiated, allowing it to decide whether or not Tk should
// be available.
//
// 3) each loadable gets its own safe slave interpreter, derived from
// the toplevel's interpreter.
// NOTE: initially the slave interpreters are not actually safe. It
// is not clear in the long term to what extent per-loadable
// interpreters need to be sandboxes, there are issues such as
// doing the equivalent of autoconf tests.
// Tcl 8.4 involved various incompatible API changes related to
// const vs. non-const data. #define'ing USE_NON_CONST or
// USE_COMPAT_CONST avoids some of the problems, but does not
// help much for C++.
#if (TCL_MAJOR_VERSION > 8) || ((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 4))
# define CDL_TCL_CONST_CAST(type,var) (var)
#else
# define CDL_TCL_CONST_CAST(type,var) const_cast<type>(var)
#endif
class CdlInterpreterBody
{
friend class CdlTest;
public:
CYGDBG_DECLARE_MEMLEAK_COUNTER();
// This is how a top-level (i.e. per-toplevel) interpreter
// should get created.
static CdlInterpreter make(Tcl_Interp* = 0);
// Create a slave interpreter for reading in the data in e.g. a
// cdl_package
CdlInterpreter create_slave(CdlLoadable, bool /* safe */ = true);
// Make the interpreter safe, a one-way operation.
void make_safe();
// The destructor is public.
virtual ~CdlInterpreterBody();
// Add or remove commands from an interpreter. This provides
// a more C++-friendly implementation of Tcl's
// CreateCommand() and DeleteCommand().
void add_command(std::string, CdlInterpreterCommand);
void remove_command(std::string);
// In the libcdl world it is also convenient to swap whole sets of
// commands in and out. This is achieved by push and pop operations.
// push returns the old set (0 at the toplevel). pop restores
// the old set.
std::vector<CdlInterpreterCommandEntry>* push_commands(std::vector<CdlInterpreterCommandEntry>&);
void pop_commands(std::vector<CdlInterpreterCommandEntry>*);
std::vector<CdlInterpreterCommandEntry>* get_pushed_commands() const;
// Similarly, allow variables to be set, unset and queried
void set_variable(std::string, std::string);
void unset_variable(std::string);
std::string get_variable(std::string);
// FIXME: add support for variable traces. These are needed
// for cdl_value and similar utilities.
// Provide hooks into the AssocData() facilities associated with
// Tcl interpreters. This makes it possible to store arbitrary
// data with an interpreter, e.g. to keep track of current state.
void set_assoc_data(const char*, ClientData, Tcl_InterpDeleteProc* =0);
void delete_assoc_data(const char*);
ClientData get_assoc_data(const char*);
// Evaluate a string as Tcl code. The return value comes from Tcl, e.g.
// TCL_OK or TCL_ERROR. There are variants depending on whether or not
// the result string is of interest.
int eval(std::string);
int eval(std::string, std::string&);
// Ditto for any Tcl code that comes from CDL files
int eval_cdl_code(const cdl_tcl_code);
int eval_cdl_code(const cdl_tcl_code, std::string&);
// And support for evaluating an entire file
int eval_file(std::string);
int eval_file(std::string, std::string&);
// For use by commands implemented in C++, a way of setting the result
void set_result(std::string);
// And a utility to get the result as well.
std::string get_result();
// Was the result set by the Tcl interpreter or by libcdl?
bool result_set_by_cdl();
// A utility to quote data that is going to end up in a TCL script.
static std::string quote(std::string);
// Turn some multiline data into a comment.
static std::string multiline_comment(const std::string&, int, int = 0);
// Add some data to a comment, allowing for newlines if necessary
static std::string extend_comment(const std::string&, int, int = 0);
// Write some data to a savefile, throwing an exception on error
void write_data(Tcl_Channel, std::string);
// File-related utilities.
void locate_subdirs(std::string, std::vector<std::string>&);
void locate_all_subdirs(std::string, std::vector<std::string>&);
void locate_files(std::string, std::vector<std::string>&);
void locate_all_files(std::string, std::vector<std::string>&);
bool is_directory(std::string);
bool is_file(std::string);
// When parsing a CDL script it is convenient to keep track of
// a number of items:
//
// 1) the toplevel, e.g. the entire configuration
// 2) the loadable, e.g. the current package
// 3) the parent of whatever is being processed at the moment
// 4) the entity, i.e. the thingamajig that is being processed.
// 5) the current file
// 6) an error reporting function
//
// This gives the various commands embedded in the Tcl interpreter
// enough information to do their job. Additional information can
// be provided via assoc_data()
//
// There should be only one call to set_toplevel(), for the
// master interpreter. All slaves inherit this, and the toplevel
// cannot be changed again.
//
// The loadable field is filled in via make_slave()
//
// For some members push and pop functions are more appropriate
// than set.
CdlToplevel get_toplevel() const;
CdlLoadable get_loadable() const;
CdlContainer get_container() const;
CdlNode get_node() const;
std::string get_context() const;
CdlDiagnosticFnPtr get_error_fn_ptr() const;
CdlDiagnosticFnPtr get_warning_fn_ptr() const;
CdlTransaction get_transaction() const;
void set_toplevel(CdlToplevel);
void set_transaction(CdlTransaction);
CdlContainer push_container(CdlContainer);
void pop_container(CdlContainer);
CdlNode push_node(CdlNode);
void pop_node(CdlNode);
std::string push_context(std::string);
void pop_context(std::string);
CdlDiagnosticFnPtr push_error_fn_ptr(CdlDiagnosticFnPtr);
void pop_error_fn_ptr(CdlDiagnosticFnPtr);
CdlDiagnosticFnPtr push_warning_fn_ptr(CdlDiagnosticFnPtr);
void pop_warning_fn_ptr(CdlDiagnosticFnPtr);
// Provide utility classes for common push/pop combinations. The
// push happens during the constructor, the pop during the
// destructor. This can simplify some code, especially when
// exceptions may get thrown.
class DiagSupport {
public:
DiagSupport(CdlInterpreter interp_arg, CdlDiagnosticFnPtr error_fn_arg, CdlDiagnosticFnPtr warn_fn_arg) {
interp = interp_arg;
saved_error_fn = interp->push_error_fn_ptr(error_fn_arg);
saved_warn_fn = interp->push_warning_fn_ptr(warn_fn_arg);
}
~DiagSupport() {
interp->pop_error_fn_ptr(saved_error_fn);
interp->pop_warning_fn_ptr(saved_warn_fn);
}
private:
DiagSupport();
CdlInterpreter interp;
CdlDiagnosticFnPtr saved_error_fn;
CdlDiagnosticFnPtr saved_warn_fn;
};
class ContextSupport {
public:
ContextSupport(CdlInterpreter interp_arg, std::string context) {
interp = interp_arg;
saved_context = interp->push_context(context);
}
~ContextSupport() {
interp->pop_context(saved_context);
}
private:
ContextSupport();
CdlInterpreter interp;
std::string saved_context;
};
class ContainerSupport {
public:
ContainerSupport(CdlInterpreter interp_arg, CdlContainer container) {
interp = interp_arg;
saved_container = interp->push_container(container);
}
~ContainerSupport() {
interp->pop_container(saved_container);
}
private:
ContainerSupport();
CdlInterpreter interp;
CdlContainer saved_container;
};
class NodeSupport {
public:
NodeSupport(CdlInterpreter interp_arg, CdlNode node) {
interp = interp_arg;
saved_node = interp->push_node(node);
}
~NodeSupport() {
interp->pop_node(saved_node);
}
private:
NodeSupport();
CdlInterpreter interp;
CdlNode saved_node;
};
class CommandSupport {
public:
CommandSupport(CdlInterpreter interp_arg, std::vector<CdlInterpreterCommandEntry>& commands) {
interp = interp_arg;
saved_commands = interp->push_commands(commands);
}
CommandSupport(CdlInterpreter interp_arg, CdlInterpreterCommandEntry* commands) {
unsigned int i;
for (i = 0; 0 != commands[i].command; i++) {
new_commands.push_back(commands[i]);
}
interp = interp_arg;
saved_commands = interp->push_commands(new_commands);
}
~CommandSupport() {
interp->pop_commands(saved_commands);
}
private:
CommandSupport();
CdlInterpreter interp;
std::vector<CdlInterpreterCommandEntry>* saved_commands;
std::vector<CdlInterpreterCommandEntry> new_commands;
};
// Similar utility classes for variables and assoc data.
class VariableSupport {
public:
VariableSupport(CdlInterpreter interp_arg, std::string varname_arg, std::string data) {
interp = interp_arg;
varname = varname_arg;
interp->set_variable(varname, data);
}
~VariableSupport() {
interp->unset_variable(varname);
}
private:
VariableSupport();
CdlInterpreter interp;
std::string varname;
};
class AssocSupport {
public:
AssocSupport(CdlInterpreter interp_arg, const char* name_arg, ClientData data, Tcl_InterpDeleteProc* del_proc = 0) {
interp = interp_arg;
name = name_arg;
interp->set_assoc_data(name, data, del_proc);
}
~AssocSupport() {
interp->delete_assoc_data(name);
}
private:
AssocSupport();
CdlInterpreter interp;
const char* name;
};
// Some command implementations may want to access other Tcl library
// routines such as Tcl_SplitList(). This requires convenient access
// to the underlying Tcl interpreter.
Tcl_Interp* get_tcl_interpreter() const;
// For use by the assertion macros.
bool check_this(cyg_assert_class_zeal = cyg_quick) const;
private:
// This is the Tcl command proc that gets registered for all
// CdlInterpreterCommand instances.
static int tcl_command_proc(ClientData, Tcl_Interp*, int, const char*[]);
// This key is used to access the CdlInterpreter assoc data.
static char* cdlinterpreter_assoc_data_key;
// Do not allow static instances of a Cdl interpreter. There are too
// many possible failure conditions. Cdl interpreters can only be
// created dynamically via make(), which will invoke this.
CdlInterpreterBody(Tcl_Interp*);
// Default constructor, copy constructor and assignment are illegal
CdlInterpreterBody();
CdlInterpreterBody(const CdlInterpreterBody&);
CdlInterpreterBody& operator=(const CdlInterpreterBody&);
Tcl_Interp* tcl_interp; // The underlying Tcl interpreter
bool owns_interp; // Was the Tcl interpreter created by the library?
std::vector<CdlInterpreter> slaves; // All slave interpreters
CdlInterpreter parent; // Or else the parent
CdlToplevel toplevel; // Data that gets used during the parsing process
CdlTransaction transaction;
CdlLoadable loadable;
CdlContainer container;
CdlNode node;
std::string context;
CdlDiagnosticFnPtr error_fn_ptr;
CdlDiagnosticFnPtr warning_fn_ptr;
bool cdl_result;
std::vector<CdlInterpreterCommandEntry>* current_commands; // for push() and pop()
enum {
CdlInterpreterBody_Invalid = 0,
CdlInterpreterBody_Magic = 0x0be67689
} cdlinterpreterbody_cookie;
};
//}}}
//{{{ CdlReference/Referrer classes
// ---------------------------------------------------------------------------
// CDL objects are organised primarily in a tree hierarchy. For
// example a package contains components, components contain options,
// and so on. The tree hierarchy tends to change rather infrequently,
// so it makes sense to have a quick way of navigating between
// entities without continuously having to do hash-table lookups. In
// addition it is very desirable to make the connectivity
// bidirectional: if a "requires" property in option A references
// option B then it would be useful to have a link back to A from B;
// that way, if the value of B changes it is a lot easier to keep
// things up to date.
//
// Terminology: the entity which contains the reference, e.g. a
// "requires" property, is the source. The relevant property is the
// "source property". The entity pointed at is the destination.
//
// Unfortunately there may be connections between CDL entities outside
// the tree hierarchy. In particular any property can contain one or
// more references to packages, components, options, wizards, or
// whatever. Often these references will be to options etc. within the
// same package, but some references will go to other packages. There
// may even be references to other configurations: for example a board
// may contain both an ordinary processor and a DSP; these two need
// their own configurations; however a package running on the DSP may
// need to interact with a package running on the processor, and vice
// versa.
//
// Also, a reference may occur inside an object that is not in the
// hierarchy. For example CDL expressions may get evaluated inside Tcl
// code rather than as part of a property. Such expressions may still
// contain references to entities in the current configuration.
//
// References may not be resolved. When reading in a CDL script there
// may be forward references. A reference may involve another package
// that has not yet been loaded, which is a conflict.
//
// Using simple pointers to store these connections is a bad idea. It
// makes it a lot harder to figure out what is connected to what, and
// it introduces horrible consistency problems when packages get
// loaded and unloaded. Instead libCDL provides a CdlReference class.
// Whenever a CdlProperty contains a reference to some other CDL
// entity there should be a CdlReference object corresponding to this.
// The reverse direction is handled via a CdlReferrer object.
//
// A CdlReference object can be either bound or unbound. By default it
// is unbound, containing only a string. It can then be bound via a
// member function, examined, and unbound again as required. Creating
// a binding automatically creates a CdlReferrer entry in the target
// object, thus avoiding any risk of inconsistencies.
//
// The CdlReference class should not be used outside the hierarchy,
// since every bound reference must have a referrer object pointing
// back, and this link back can only be valid within the hierarchy.
// Temporary CdlReference objects are useful during the construction
// of properties.
//
// It is possible that a given property (e.g. a complicated "requires"
// expression) has multiple references to another entity. Each of
// these involves a separate CdlReference/CdlReferrer pair.
// ----------------------------------------------------------------------------
// The actual CdlReference class.
class CdlReference {
friend class CdlTest;
// CdlReferrer must be a friend so that when a package gets unloaded
// it can clean up all references to it.
friend class CdlReferrer;
public:
// The default constructor should not normally be used, instead
// a string should be supplied. However there are vectors of
// reference objects...
CdlReference();
// The main constructor supplies the name of the referenced
// entity. The resulting object will be unbound.
CdlReference(const std::string);
// The copy constructor is legal for unbound objects only.
CdlReference(const CdlReference&);
// The assignment operator is needed for STL operations.
// Again it only makes sense of unbound objects.
CdlReference& operator=(const CdlReference&);
// The destructor is only valid for unbound objects. All references
// should be unbound before an entity can be destroyed.
~CdlReference();
// Access the various fields.
void set_destination_name(const std::string);
const std::string& get_destination_name() const;
CdlNode get_destination() const;
// Binding a reference. Obviously this can only be used when the
// reference is still unbound. When doing the binding it is
// necessary to know:
// (1) the object containing the reference.
// (2) the specific property that contains the reference.
// (3) the object being referred to.
// Binding a reference results in a new referrer entry in the
// destination.
void bind(CdlNode, CdlProperty, CdlNode);
// Unbinding a reference. Typically this only happens when the
// destination is unloaded. The arguments provide the source and
// the source property.
void unbind(CdlNode, CdlProperty);
// This is used by the ASSERT_CLASS() and ASSERT_THIS() macros.
bool check_this(cyg_assert_class_zeal cyg_quick) const;
CYGDBG_DECLARE_MEMLEAK_COUNTER();
protected:
private:
// The data fields. The name is usually filled in by the
// constructor. The destination defaults to zero for an unbound
// object and gets filled in by the bind() operation.
std::string dest_name;
CdlNode dest;
enum {
CdlReference_Invalid = 0,
CdlReference_Magic = 0x3f908608
} cdlreference_cookie;
};
// ----------------------------------------------------------------------------
// The CdlNode class (and hence just about everything) contains a
// vector of CdlReferrer objects. This keeps track of all entities
// that refer to this one, so if the value associated with this
// changes it is possible to work out the impact of this on all
// entities that rely on this value.
//
// Arguably this should work in terms of CdlValuable objects rather
// than CdlNode objects. However it is convenient to use references
// for the connection between e.g. an option and a dialog, where
// there is no value involved. The reverse connection is of little
// use in this circumstance.
//
// CdlReferrer objects are rarely accessed directly. Instead they will
// be filled in during a CdlReference::bind() operation and erased
// during a CdlReference::unbind() operation. The only operations that
// should be public allow access to the contained data.
class CdlReferrer {
friend class CdlTest;
// CdlReference::bind() and unbind() have direct access to the
// members, since these two functions are really responsible for
// creating and destroying referrer objects.
friend class CdlReference;
public:
// The default constructor, copy constructor and assignment
// operator are all public to avoid problems with having vectors
// of referrer objects. Similarly the destructor is public.
// In practice updates actually happen as a consequence of
// CdlReference::bind() and CdlReference::unbind().
CdlReferrer();
CdlReferrer(const CdlReferrer&);
CdlReferrer& operator=(const CdlReferrer&);
~CdlReferrer();
CdlNode get_source() const;
CdlProperty get_source_property() const;
void update(CdlTransaction, CdlNode, CdlUpdate);
bool check_this(cyg_assert_class_zeal=cyg_quick) const;
CYGDBG_DECLARE_MEMLEAK_COUNTER();
private:
CdlNode source;
CdlProperty source_property;
enum {
CdlReferrer_Invalid = 0,
CdlReferrer_Magic = 0x70e1fc37
} cdlreferrer_cookie;
};
//}}}
//{{{ Value and Expression classes
//{{{ CdlEvalContext
// ----------------------------------------------------------------------------
// Expression evaluation always happens within a certain context.
// This may involve a transaction. Usually it involves a node and
// a property within that node, although it is possible to evaluate
// expressions from inside Tcl code.
//
// To avoid passing too many arguments around the various
// evaluation-related routines, a utility class is provided.
class CdlEvalContext {
friend class CdlTest;
public:
CdlTransaction transaction;
CdlNode node;
CdlProperty property;
CdlToplevel toplevel;
CdlEvalContext(CdlTransaction, CdlNode = 0, CdlProperty = 0, CdlToplevel = 0);
~CdlEvalContext();
// Given a reference inside an expression, try to resolve this to either
// a node or, more specifically, a valuable.
CdlNode resolve_reference(CdlExpression, int);
CdlValuable resolve_valuable_reference(CdlExpression, int);
bool check_this(cyg_assert_class_zeal = cyg_quick) const;
CYGDBG_DECLARE_MEMLEAK_COUNTER();
protected:
private:
// Illegal operation, the three fields must always be supplied,
// although they may be zero.
CdlEvalContext();
enum {
CdlEvalContext_Invalid = 0,
CdlEvalContext_Magic = 0x03434be9
} cdlevalcontext_cookie;
};
//}}}
//{{{ CdlSimpleValue
// ----------------------------------------------------------------------------
// Expression evaluation happens in terms of CdlSimpleValue objects.
// In CDL all values are strings, but for the purposes of arithmetic
// these strings sometimes have to be interpreted as integers or as
// double precision numbers. Sometimes there is a choice, for example
// the equality operator == can mean numerical or string comparison.
// The basic rules that get applied are:
//
// 1) if the current value has an integer representation then
// use this by preference. This means that an expression
// of the form (CYGNUM_XXX != 0x100) will do a integer
// comparison if possible.
//
// 2) otherwise if the current value can be interpreted as a
// double precision number, use that representation.
// All integers can be interpreted as doubles (at the risk
// of some loss of precision), so the representation as
// a double should only be used if the integer representation
// is inappropriate.
//
// 3) otherwise interpret the value as a string.
//
// The default value is 0.
class CdlSimpleValue {
friend class CdlTest;
public:
CdlSimpleValue();
CdlSimpleValue(std::string);
CdlSimpleValue(cdl_int);
CdlSimpleValue(double);
CdlSimpleValue(const CdlSimpleValue&);
CdlSimpleValue(bool);
~CdlSimpleValue();
CdlSimpleValue& operator=(const CdlSimpleValue&);
CdlSimpleValue& operator=(std::string);
CdlSimpleValue& operator=(cdl_int);
CdlSimpleValue& operator=(double);
CdlSimpleValue& operator=(bool);
bool operator==(const CdlSimpleValue&) const;
bool operator!=(const CdlSimpleValue&) const;
bool operator==(std::string arg) const
{
CdlSimpleValue val(arg);
return *this == val;
}
bool operator==(cdl_int arg) const
{
CdlSimpleValue val(arg);
return *this == val;
}
bool operator==(double arg) const
{
CdlSimpleValue val(arg);
return *this == val;
}
bool operator!=(std::string arg) const
{
CdlSimpleValue val(arg);
return *this != val;
}
bool operator!=(cdl_int arg) const
{
CdlSimpleValue val(arg);
return *this != val;
}
bool operator!=(double arg) const
{
CdlSimpleValue val(arg);
return *this != val;
}
void set_value(std::string, CdlValueFormat = CdlValueFormat_Default);
std::string get_value() const;
bool has_integer_value() const;
void set_integer_value(cdl_int, CdlValueFormat = CdlValueFormat_Default);
cdl_int get_integer_value() const;
bool has_double_value() const;
void set_double_value(double, CdlValueFormat = CdlValueFormat_Default);
double get_double_value() const;
CdlValueFormat get_value_format() const;
void set_value_format(CdlValueFormat);
void set_value_format(CdlSimpleValue&);
void set_value_format(CdlSimpleValue&, CdlSimpleValue&);
static void eval_valuable(CdlEvalContext&, CdlValuable, CdlSimpleValue&);
// For expression evaluation, it is often convenient to get hold
// of a boolean as well. This may indicate a non-empty string
// or a non-zero value.
bool get_bool_value() const;
// This class is too simple to warrant even a cookie validation.
bool check_this(cyg_assert_class_zeal zeal = cyg_quick) const {
return true;
}
protected:
private:
enum {
int_valid = 0x01,
double_valid = 0x02,
string_valid = 0x04,
int_invalid = 0x08,
double_invalid = 0x10
};
mutable int valid_flags;
mutable std::string value;
mutable cdl_int int_value;
mutable double double_value;
CdlValueFormat format;
};
//}}}
//{{{ CdlListValue
// ----------------------------------------------------------------------------
// Evaluating a list expression results in a set of possible values, but
// unlike the original list expression these values are now constant and
// can have no dependencies on CDL entities. As with list expressions the
// main operation on a list value is to detect membership, but using
// list values allows multiple potential members to be tested without
// repeated expression evaluation. The main use of list values is implicit
// in libcdl, each list expression contains a mutable cached list value.
//
// A list value contains five sets of data:
//
// 1) separate vectors of strings, integers, and floating point constants.
// Having separate vectors of integers and floating points avoids
// problems when numbers can be represented in different formats.
// 2) a vector of cdl_int pairs for ranges of integer data
// 3) a vector of double pairs for ranges of floating point data
//
// Any of these vectors may be empty, but at least one of the vectors should
// contain useful data. Possibly there should also be tables for cdl_int and
// double to avoid unnecessary string conversions.
class CdlListValue {
friend class CdlTest;
// A list value will only be filled in when a list expression is evaluated.
// The members cannot be updated by other means.
friend class CdlListExpressionBody;
public:
CdlListValue();
~CdlListValue();
CdlListValue(const CdlListValue&);
CdlListValue& operator=(const CdlListValue&);
bool is_member(CdlSimpleValue&) const;
bool is_member(std::string, bool = true) const;
bool is_member(cdl_int, bool = true) const;
bool is_member(double, bool = true) const;
// These provide access to the raw data, for example if it is
// necessary to suggest a legal value to the user.
const std::vector<CdlSimpleValue>& get_table() const;
const std::vector<std::pair<cdl_int, cdl_int> >& get_integer_ranges() const;
const std::vector<std::pair<double, double> >& get_double_ranges() const;
bool check_this(cyg_assert_class_zeal = cyg_quick) const;
CYGDBG_DECLARE_MEMLEAK_COUNTER();
private:
std::vector<CdlSimpleValue> table;
std::vector<std::pair<cdl_int, cdl_int> > integer_ranges;
std::vector<std::pair<double, double> > double_ranges;
enum {
CdlListValue_Invalid = 0,
CdlListValue_Magic = 0x2183a943
} cdllistvalue_cookie;
};
//}}}
//{{{ CdlValue
// ----------------------------------------------------------------------------
// Values in CDL are non-trivial compared with some other languages.
// Even though CDL is not a fully-typed language, it does still have
// four different flavors to consider. There is also the problem that
// an entity may have up to four different values which should be
// stored (default, inferred, wizard, user), with the ability to
// switch between them. The CdlValue class provides support for this.
//
// CdlValue objects are not normally updated explicitly. Instead
// higher level code deals with CdlValuable objects, which inherit
// privately from CdlValue. Modifications to CdlValuables happen in
// the context of a transaction.
//
// The first concept to take into account is the flavor. There
// are four flavors, None, Bool, BoolData and Data. The member
// function get_flavor() can be used to obtain the current flavor.
//
// CdlValueFlavor CdlValue::get_flavor() const;
//
// Values may be enabled or disabled. Values of flavor None
// and Data are always enabled. Values of flavor Bool or BoolData
// may or may not be enabled, by default they are disabled.
//
// bool CdlValue::is_enabled(...) const;
//
// (The optional argument to is_enabled() is discussed later).
//
// Values of flavor BoolData and Data also have a string value,
// which can be interpreted as an integer or a double under
// the right circumstances.
//
// std::string CdlValue::get_value(...) const;
// bool CdlValue::has_integer_value(...) const;
// bool CdlValue::has_double_value(...) const;
// cdl_int CdlValue::get_integer_value(...) const;
// double CdlValue::get_double_value(...) const;
//
// This is equivalent to a CdlSimpleValue object, and in fact
// that is the internal representation. It is possible to
// get hold of the CdlSimpleValue object directly:
//
// CdlSimpleValue CdlValue::get_simple_value(...) const;
//
// The get_integer_value() and get_double_value() members should
// only be used if you are confident that the current value has
// an integer or double representation. Otherwise the result is
// undefined.
//
// The optional argument to these member functions represents
// the source. A value can be set from four different sources:
// the default value (usually either 0 or the result of
// evaluating a default_value property); an inferred value,
// determined by the inference engine; a wizard value, i.e.
// what a CDL wizard believes the correct value to be based
// on user input; and a user value, something explicitly
// set by the end user. These have different priorities:
// the inference engine can override default values but not
// user values. A CdlValue object keeps track of the current
// source.
//
// CdlValueSource CdlValue::get_source() const;
//
// If no argument is given to e.g. is_enabled() then the
// current source is used. Otherwise it is possible to find
// out whether or not the entity is enabled for each of the
// sources.
//
// The default source is always defined, the others may or
// may not be. It is possible to find out for each source
// whether or not a value has been set.
//
// bool CdlValue::has_source(CdlValueSource) const;
//
//
// Updating values normally happens in the CdlValuable class,
// but the member functions are the same. There is a member
// function to change the flavor:
//
// void CdlValue::set_flavor(CdlValueFlavor);
//
// However this member function is intended only for use by the
// library itself. An entity's flavor is normally defined by CDL data,
// and should not be updated explicitly by application code.
//
// There are two member functions to manipulate the value source:
//
// void CdlValue::set_source(CdlValueSource);
// void CdlValue::invalidate_source(CdlValueSource);
//
// The first function can be used if e.g. the user wants to
// change his or her mind and go back to the default value
// rather than a user value. The user value is not forgotten
// and can be reinstated.
//
// invalidate_source() can be used to completely cancel a
// value source. If that source happens to be the current one
// then the current source will be adjusted appropriately.
// It is illegal to attempt to invalidate the default source.
//
// For values with flavor Bool and BoolData, there are three
// member functions that can be used to control the enabled
// status:
//
// void CdlValue::set_enabled(bool, CdlValueSource);
// void CdlValue::enable(CdlValueSource);
// void CdlValue::disable(CdlValueSource);
//
// Note that when updating a CdlValue object the source should
// be known and must be specified. If the source has a higher
// priority than the current one then it will automatically
// become the new source. On the rare occasion that this is
// not desired, set_source() will have to be used afterwards
// to reset the current source.
//
// For values with flavor BoolData and Data the following
// member functions are available to change the value string:
//
// void CdlValue::set_value(std::string, CdlValueSource);
// void CdlValue::set_value(cdl_int, CdlValueSource);
// void CdlValue::set_value(double, CdlvalueSource);
// void CdlValue::set_value(CdlSimpleValue&, CdlValueSource);
//
// For values with flavor BoolData is is possible to
// combine updating the enabled flag and the string value:
//
// void CdlValue::set_enabled_and_value(bool, std::string, CdlValueSource);
// void CdlValue::set_enabled_and_value(bool, cdl_int, CdlValueSource);
// void CdlValue::set_enabled_and_value(bool, double, CdlValueSource);
// void CdlValue::set_enabled_and_value(bool, CdlSimpleValue&, CdlValueSource);
// void CdlValue::enable_and_set_value(std::string, CdlValueSource);
// void CdlValue::enable_and_set_value(cdl_int, CdlValueSource);
// void CdlValue::enable_and_set_value(double, CdlValueSource);
// void CdlValue::enable_and_set_value(CdlSimpleValue&, CdlValueSource);
// void CdlValue::disable_and_set_value(std::string, CdlValueSource);
// void CdlValue::disable_and_set_value(cdl_int, CdlValueSource);
// void CdlValue::disable_and_set_value(double, CdlValueSource);
// void CdlValue::disable_and_set_value(CdlSimpleValue&, CdlValueSource);
//
// Obviously many of these functions are just simple inlines.
//
// There is one final member function:
//
// void CdlValue::set(CdlSimpleValue, CdlValueSource);
//
// This member function is defined to do the right thing,
// whatever the flavor happens to be.
class CdlValue {
friend class CdlTest;
public:
CdlValue(CdlValueFlavor = CdlValueFlavor_Bool);
virtual ~CdlValue();
CdlValue(const CdlValue&);
CdlValue& operator=(const CdlValue&);
CdlValueFlavor get_flavor() const;
CdlValueSource get_source() const;
bool has_source(CdlValueSource) const;
bool is_enabled(CdlValueSource = CdlValueSource_Current) const;
std::string get_value(CdlValueSource = CdlValueSource_Current) const;
bool has_integer_value(CdlValueSource = CdlValueSource_Current) const;
bool has_double_value(CdlValueSource = CdlValueSource_Current) const;
cdl_int get_integer_value(CdlValueSource = CdlValueSource_Current) const;
double get_double_value(CdlValueSource = CdlValueSource_Current) const;
CdlSimpleValue get_simple_value(CdlValueSource = CdlValueSource_Current) const;
void set_source(CdlValueSource);
void invalidate_source(CdlValueSource);
void set_enabled(bool, CdlValueSource);
void enable(CdlValueSource source)
{
set_enabled(true, source);
}
void disable(CdlValueSource source)
{
set_enabled(false, source);
}
void set_value(CdlSimpleValue&, CdlValueSource);
void set_value(std::string data, CdlValueSource source)
{
CdlSimpleValue val(data);
set_value(val, source);
}
void set_integer_value(cdl_int data, CdlValueSource source)
{
CdlSimpleValue val(data);
set_value(val, source);
}
void set_double_value(double data, CdlValueSource source)
{
CdlSimpleValue val(data);
set_value(val, source);
}
void set_enabled_and_value(bool, CdlSimpleValue&, CdlValueSource);
void set_enabled_and_value(bool enabled, std::string data, CdlValueSource source)
{
CdlSimpleValue val(data);
set_enabled_and_value(enabled, val, source);
}
void set_enabled_and_value(bool enabled, cdl_int data, CdlValueSource source)
{
CdlSimpleValue val(data);
set_enabled_and_value(enabled, val, source);
}
void set_enabled_and_value(bool enabled, double data, CdlValueSource source)
{
CdlSimpleValue val(data);
set_enabled_and_value(enabled, val, source);
}
void enable_and_set_value(CdlSimpleValue& val, CdlValueSource source)
{
set_enabled_and_value(true, val, source);
}
void enable_and_set_value(std::string data, CdlValueSource source)
{
set_enabled_and_value(true, data, source);
}
void enable_and_set_value(cdl_int data, CdlValueSource source)
{
set_enabled_and_value(true, data, source);
}
void enable_and_set_value(double data, CdlValueSource source)
{
set_enabled_and_value(true, data, source);
}
void disable_and_set_value(CdlSimpleValue& val, CdlValueSource source)
{
set_enabled_and_value(false, val, source);
}
void disable_and_set_value(std::string data, CdlValueSource source)
{
set_enabled_and_value(false, data, source);
}
void disable_and_set_value(cdl_int data, CdlValueSource source)
{
set_enabled_and_value(false, data, source);
}
void disable_and_set_value(double data, CdlValueSource source)
{
set_enabled_and_value(false, data, source);
}
void set(CdlSimpleValue&, CdlValueSource);
bool check_this(cyg_assert_class_zeal = cyg_quick) const;
CYGDBG_DECLARE_MEMLEAK_COUNTER();
// This should only be used by the library itself.
void set_flavor(CdlValueFlavor);
protected:
private:
CdlValueFlavor flavor;
CdlValueSource current_source;
// FIXME: a static const member should be used for the array
// sizes, but VC++ does not support that part of the language.
// FIXME: std::bitset should be used here. Using lots of separate
// bools here is inefficient.
bool source_valid[4];
bool enabled[4];
CdlSimpleValue values[4];
enum {
CdlValue_Invalid = 0,
CdlValue_Magic = 0x41837960
} cdlvalue_cookie;
};
//}}}
//{{{ CdlSubexpression
// ----------------------------------------------------------------------------
// Expressions come into existence primarily as the result of reading
// in certain properties like default_value in CDL data. It is also
// possible for expressions to be generated and evaluated on the fly
// inside Tcl code, but that is expected to be a comparatively rare
// events. Expression objects always live on the heap, usually only
// in derived classes.
//
// An ordinary expression evaluates to a single value. There are two
// other types of expression in the CDL language, goal expressions and
// list expression. A goal expression is essentially a set of ordinary
// expressions with implicit &&'s between them. A list expression
// is a set of expressions that can be evaluated to a constant vector,
// plus pairs of expressions that constitute ranges. Again goal and
// list expressions only live on the heap.
//
// Both parsing an evaluation involve tokens for the various
// operators. The inference engine, conflict reporting code, and
// other diagnostic code will also need to have ready access to
// this information. Hence it makes a bit more sense to have
// the enum outside the expression class.
enum CdlExprOp {
CdlExprOp_Invalid = 0,
CdlExprOp_EOD = 1, // End of data reached
CdlEXprOp_Command = 2, // [tcl code]
CdlExprOp_Variable = 3, // $tcl_variable
CdlExprOp_StringConstant = 4, // "hello"
CdlExprOp_IntegerConstant = 5, // 123
CdlExprOp_DoubleConstant = 6, // 3.1415
CdlExprOp_Reference = 7, // CYGPKG_INFRA
CdlExprOp_Range = 8, // x to y
CdlExprOp_Negate = 9, // -x
CdlExprOp_Plus = 10, // +x
CdlExprOp_LogicalNot = 11, // !x
CdlExprOp_BitNot = 12, // ~x
CdlExprOp_Indirect = 13, // *x
CdlExprOp_Active = 14, // ?x
CdlExprOp_Function = 15, // sin(x)
CdlExprOp_Multiply = 16, // x * y
CdlExprOp_Divide = 17, // x / y
CdlExprOp_Remainder = 18, // x % y
CdlExprOp_Add = 19, // x + y
CdlExprOp_Subtract = 20, // x - y
CdlExprOp_LeftShift = 21, // x << y
CdlExprOp_RightShift = 22, // x >> y
CdlExprOp_LessThan = 23, // x < y
CdlExprOp_LessEqual = 24, // x <= y
CdlExprOp_GreaterThan = 25, // x > y
CdlExprOp_GreaterEqual = 26, // x >= y
CdlExprOp_Equal = 27, // x == y
CdlExprOp_NotEqual = 28, // x != y
CdlExprOp_BitAnd = 29, // x & y
CdlExprOp_BitXor = 30, // x ^ y
CdlExprOp_BitOr = 31, // x | y
CdlExprOp_And = 32, // x && y
CdlExprOp_Or = 33, // x || y
CdlExprOp_Cond = 34, // x ? a : b
CdlExprOp_StringConcat = 35, // x . y
CdlExprOp_Implies = 36, // x implies y
CdlExprOp_Xor = 37, // x xor y
CdlExprOp_Eqv = 38 // x eqv y
};
// ----------------------------------------------------------------------------
// A subexpression consists of an operation, possibly some constant
// data, and possibly indices into the subexpression vector.
// Normally some unions would be used, but unions and objects such
// as std::string do not mix, and the amount of memory involved is
// not big enough to really worry about.
#define CdlFunction_MaxArgs 3
struct CdlSubexpression {
CdlExprOp op;
CdlSimpleValue constants; // String, integer or double constant
int reference_index; // iff CdlExprOp_Reference
int lhs_index; // for all non-constant operators
int rhs_index; // for binary and ternary operators only
int rrhs_index; // only for ternary operators.
int func; // iff CdlExprOp_Function
int args[CdlFunction_MaxArgs];
};
//}}}
//{{{ CdlFunction
// ----------------------------------------------------------------------------
// Generic support for function parsing, evaluation, and inference. The
// implementation is extensible so that functions can be added to the
// core via static constructors.
class CdlFunction {
friend class CdlTest;
public:
CdlFunction(const char* /* name */, int /* no_args */,
void (*)(CdlExpression, const CdlSubexpression&),
void (*)(CdlEvalContext&, CdlExpression, const CdlSubexpression&, CdlSimpleValue&),
bool (*)(CdlTransaction, CdlExpression, unsigned int, bool, int),
bool (*)(CdlTransaction, CdlExpression, unsigned int, CdlSimpleValue&, int)
);
~CdlFunction();
static bool is_function(std::string, int&);
static std::string get_name(int);
static int get_args_count(int);
static void check(CdlExpression, const CdlSubexpression&);
static void eval(CdlEvalContext&, CdlExpression, const CdlSubexpression&, CdlSimpleValue&);
static bool infer_bool(CdlTransaction, CdlExpression, unsigned int, bool, int);
static bool infer_value(CdlTransaction, CdlExpression, unsigned int, CdlSimpleValue&, int);
static void (*null_check)(CdlExpression, const CdlSubexpression&);
static bool (*null_infer_bool)(CdlTransaction, CdlExpression, unsigned int, bool, int);
static bool (*null_infer_value)(CdlTransaction, CdlExpression, unsigned int, CdlSimpleValue&, int);
protected:
private:
// Keep track of all functions in the system
static std::vector<CdlFunction*> all_functions;
// Each function object is given a unique id during initialization
static int next_id;
int id;
// Provided by the constructor
const char* name;
int number_args;
void (*check_fn)(CdlExpression, const CdlSubexpression&);
void (*eval_fn)(CdlEvalContext&, CdlExpression, const CdlSubexpression&, CdlSimpleValue&);
bool (*infer_bool_fn)(CdlTransaction, CdlExpression, unsigned int, bool, int);
bool (*infer_value_fn)(CdlTransaction, CdlExpression, unsigned int, CdlSimpleValue&, int);
// The default constructor is illegal
CdlFunction();
};
//}}}
//{{{ CdlExpression
// ----------------------------------------------------------------------------
// And now for the expression class itself.
class CdlExpressionBody {
friend class CdlTest;
public:
// The default constructor is basically a no-op, new expression
// objects only get created as a consequence of parsing. However
// it exists and is protected for the convenience of derived
// classes. The copy constructor is protected, allowing parsing
// code to first parse an expression and then copy the expression
// into a higher level object. The assignment operator is illegal.
// There is no reason to hide the destructor.
virtual ~CdlExpressionBody();
// An expression involves three pieces of data. There is a vector
// of subexpressions. It is also necessary to know where
// evaluation should being, in accordance with operator precedence
// rules. And there is a vector of CdlReference objects - this
// needs to be kept separate from the subexpression vector because
// CdlReference objects are comparatively tricky.
//
// All of this data is public and can be readily inspected by the
// inference engine, by conflict detection code, by diagnostic
// code, etc.
std::vector<CdlSubexpression> sub_expressions;
int first_subexpression;
std::vector<CdlReference> references;
bool update(CdlTransaction, CdlNode, CdlProperty, CdlNode, CdlUpdate);
// There are a number of parsing functions. The first one is
// used by higher-level code to parse a single expression. Its
// argument is a single string (which may be the result of
// concatenating several Tcl arguments), and at the end of the
// parse operation there should be no further data. The result
// will either be a new expression object or a parsing exception
// to be caught by higher level code.
static CdlExpression parse(std::string);
// This is used when parsing list expressions, which involve a
// sequence of ordinary expressions and possibly range operators.
// The whole list expression lives in a single string, and it is
// necessary to provide an index indicating where in the string
// parsing should begin. It is also useful to return details of
// the token that caused parsing to terminate (EOD, Range, or
// the start of something else).
static CdlExpression parse(std::string, int&, CdlExprOp&, int&);
// A goal expression is derived from an ordinary expression but
// has somewhat different rules for evaluating. Parsing a goal
// expression involves parsing a number of ordinary expressions
// with implicit && operators between them, and it requires
// a parsing function that can be used to extend an existing
// expression.
//
// NOTE: possibly this should should be a protected member, since
// its main use is in parsing goal expressions.
static void continue_parse(CdlExpression, std::string, int&, CdlExprOp&, int&);
// Evaluating expressions. Note that this may fail at run-time
// because of errors that cannot be caught sensibly when the
// expression is read in, for example arithmetic overflow or
// division by zero. Because such failures are a possibility
// anyway no special action is taken to prevent an expression
// with e.g. an unresolved reference from being evaluated.
//
// eval() is the public interface, and manages
// CdlConflict_EvalException objects. eval_internal() is for use
// by list and goal expressions.
void eval(CdlEvalContext&, CdlSimpleValue&);
void eval_internal(CdlEvalContext&, CdlSimpleValue&);
void eval_subexpression(CdlEvalContext&, int, CdlSimpleValue&);
// The full original expression is useful for diagnostics purposes
std::string get_original_string() const;
bool check_this(cyg_assert_class_zeal cyg_quick) const;
CYGDBG_DECLARE_MEMLEAK_COUNTER();
protected:
// The default constructor does very little, the main work
// is done by the various parsing functions. However it is
// available to derived classes, especially goal expressions.
CdlExpressionBody();
// The copy constructor has to be usable by derived classes,
// e.g. CdlExpressionProperty
CdlExpressionBody(const CdlExpressionBody&);
private:
// The assignment operator is illegal.
CdlExpressionBody& operator=(const CdlExpressionBody&);
// The string that was parsed originally
std::string expression_string;
enum {
CdlExpressionBody_Invalid = 0,
CdlExpressionBody_Magic = 0x760293a3
} cdlexpressionbody_cookie;
};
//}}}
//{{{ CdlListExpression
// ----------------------------------------------------------------------------
// The main use of list expressions is for the legal_values
// properties. Essentially a list expression is just a vector of
// ordinary expressions and ranges of expressions.
class CdlListExpressionBody {
friend class CdlTest;
public:
// Availability of constructors etc. is as per the ordinary
// expression class.
virtual ~CdlListExpressionBody();
// The data associated with a list expression is a vector of
// expressions, plus a vector of expression pairs constituting
// ranges. As with ordinary expressions the data is fully public
// and can be readily examined by e.g. the inference engine.
std::vector<CdlExpression> data;
std::vector<std::pair<CdlExpression,CdlExpression> > ranges;
// Parsing. This involves taking a single string, typically from
// a CDL script, and parsing one or more ordinary expressions.
static CdlListExpression parse(std::string);
// Evaluation support. A list expression evaluates to a list value.
void eval(CdlEvalContext&, CdlListValue&);
// More commonly client code is going to be interested in whether
// or not a particular value is a legal member. The result
// cache ensures that it is possible to
bool is_member(CdlEvalContext&, CdlSimpleValue&);
bool is_member(CdlEvalContext&, std::string);
bool is_member(CdlEvalContext&, cdl_int);
bool is_member(CdlEvalContext&, double);
// The full original expression is useful for diagnostics purposes
std::string get_original_string() const;
bool check_this(cyg_assert_class_zeal = cyg_quick) const;
CYGDBG_DECLARE_MEMLEAK_COUNTER();
protected:
CdlListExpressionBody(const CdlListExpressionBody&);
bool update(CdlTransaction, CdlNode, CdlProperty, CdlNode, CdlUpdate);
private:
CdlListExpressionBody();
CdlListExpressionBody& operator=(const CdlListExpressionBody&);
void eval_internal(CdlEvalContext&, CdlListValue&);
std::string expression_string;
enum {
CdlListExpressionBody_Invalid = 0,
CdlListExpressionBody_Magic = 0x7da4bcc2
} cdllistexpressionbody_cookie;
};
//}}}
//{{{ CdlGoalExpression
// ----------------------------------------------------------------------------
// A goal expression inherits privately from ordinary expressions. Essentially
// a goal expression is simply a set of ordinary expressions separated by &&,
// but it can only be evaluated to a boolean. The parse() and eval() members
// of the base class should not be exposed. There is a member to get hold of
// the underlying ordinary expression, for use by e.g. the inference engine.
class CdlGoalExpressionBody : private CdlExpressionBody {
friend class CdlTest;
typedef CdlExpressionBody inherited;
public:
virtual ~CdlGoalExpressionBody();
static CdlGoalExpression parse(std::string);
// A few variants of the eval() member, with a choice of returning
// by value or by reference. The latter provide consistency with the
// other expression classes.
bool eval(CdlEvalContext&);
void eval(CdlEvalContext&, bool&);
// Provide public access to the underlying expression object,
// useful for the inference engine
CdlExpression get_expression();
// The full original expression is useful for diagnostics purposes
std::string get_original_string() const;
bool check_this(cyg_assert_class_zeal = cyg_quick) const;
CYGDBG_DECLARE_MEMLEAK_COUNTER();
protected:
CdlGoalExpressionBody(const CdlGoalExpressionBody&);
private:
CdlGoalExpressionBody();
CdlGoalExpressionBody& operator=(const CdlGoalExpressionBody&);
void eval_internal(CdlEvalContext&, bool&);
std::string expression_string;
enum {
CdlGoalExpressionBody_Invalid = 0,
CdlGoalExpressionBody_Magic = 0x5a58bb24
} cdlgoalexpressionbody_cookie;
};
//}}}
//{{{ CdlInfer
// ----------------------------------------------------------------------------
// A utility class related to inference. This exports the main functions
// needed, allowing e.g. per-function inference routines from func.cxx to
// interact with the main inference engine.
class CdlInfer {
public:
static bool make_active(CdlTransaction, CdlNode, int /* level */);
static bool make_inactive(CdlTransaction, CdlNode, int /* level */);
static bool set_valuable_value(CdlTransaction, CdlValuable, CdlSimpleValue&, int /* level */);
static bool set_valuable_bool(CdlTransaction, CdlValuable, bool, int /* level */);
static bool subexpr_value(CdlTransaction, CdlExpression, unsigned int /* index */, CdlSimpleValue& goal, int /* level */);
static bool subexpr_bool(CdlTransaction, CdlExpression, unsigned int /* index */, bool, int /* level */);
private:
CdlInfer();
};
//}}}
//}}}
//{{{ CdlConflict classes
// ----------------------------------------------------------------------------
// As a configuration is created and modified there will be times when
// things are not completely consistent. There may be a reference to
// some option that is not in any package in the current
// configuration. An option may have an invalid value, possibly as a
// side effect of a change to some other option. There may be a
// dependency that is not satisfied. There may be other types of
// conflict.
//
// The library provides a base class CdlConflict, and a number of
// derived classes for common types of conflict such as unresolved
// references. All conflicts are associated with a CdlNode and a
// property within that. It is possible to use dynamic_cast<> to find
// out the exact type of a conflict, or alternatively to use the
// virtual member function get_explanation().
//
// Conflicts may be disabled by the user if they are not actually
// important as far as application code is concerned. In other words
// the end user is allowed to override the constraints specified in
// the CDL. This information is saved with the configuration data.
// Preferably the user should give an explanation for why the conflict
// is disabled, to serve as a reminder some months later when the
// configuration is reloaded.
//
//
// Conflicts have a fairly complicated life cycle. First it is
// necessary to distinguish between structural and normal conflicts. A
// structural conflict is typically caused by a reference to a
// non-existent valuable. These conflicts are generally created only
// when something is loaded, and only go away when something is
// unloaded. A normal conflict is typically related to a value, for
// example a value outside the legal range, or a "requires" property
// that is not satisfied.
//
// Conflicts are created and destroyed in the context of a
// transaction, which in turn operates in the context of a toplevel.
// If the transaction is committed then new conflicts get added to the
// appropriate toplevel list, and destroyed conflicts get removed from
// the toplevel list. The transaction field indicates whether the
// conflict is currently per-transaction or global.
//
// Transactions may get nested, i.e. a conflict may get created as
// part of a sub-transaction, and when that sub-transaction is committed
// the conflict is moved to the parent transaction.
//
// For each toplevel, libcdl keeps track of all conflicts. This only
// applies to committed conflicts, per-transaction conflicts are not
// accessible in this way.
//
// As part of a transaction, libcdl may attempt to find solutions for
// particular conflicts, and those solutions may get installed
// automatically. No attempt is made to keep track of solutions
// on a global basis, only on a per-transaction basis.
class CdlConflictBody {
friend class CdlTest;
// Transactions and conflicts are closely connected
friend class CdlTransactionBody;
public:
// Creation happens only inside a derived class.
// Clearing a conflict only happens inside transactions.
// Destroying a conflict only happens from inside a
// per-transaction clear(), or during a transaction commit.
// Is this conflict part of a transaction, or has it been committed to the toplevel.
CdlTransaction get_transaction() const;
// Is inference implemented for this type of conflict?
virtual bool resolution_implemented() const;
// Try to resolve an existing global conflict. A new transaction
// is created for this operation, the conflict is resolved within
// that transaction, and then CdlTransaction::body() is used to
// handle inference callbacks, commits, etc. See also
// CdlToplevel::resolve_conflicts() and
// CdlToplevel::resolve_all_conflicts(). The conflict may cease to
// exist as a side-effect of this call.
void resolve();
// Keep track of whether or not this conflict has a solution
// 1) a conflict may have a current solution. This gets invalidated
// whenever there is a change to a value that was referenced
// while identifying the solution.
//
// A current solution is indicated by a non-empty solution vector.
//
// 2) a conflict may not have a current solution. Again this gets
// invalidated whenever a referred value changes. There is a boolean
// to keep track of this.
//
// 3) a conflict may not have a current solution, but another run of
// the inference engine may find one.
bool has_known_solution() const;
bool has_no_solution() const;
const std::vector<std::pair<CdlValuable, CdlValue> >& get_solution() const;
const std::set<CdlValuable>& get_solution_references() const;
void clear_solution();
// Provide a text message "explaining" the conflict.
// This only makes sense for derived classes.
virtual std::string get_explanation() const = 0;
// Basic information access.
CdlNode get_node() const;
CdlProperty get_property() const;
bool is_structural() const;
// Enabling and disabling conflicts currently happens outside the
// context of any transaction.
// FIXME: these are not currently implemented. It would be necessary
// to store the information in the savefile, which requires an
// unambiguous way of identifying a conflict that is likely to
// survice package version changes.
void disable(std::string);
void enable();
bool is_enabled() const;
std::string get_disabled_reason() const;
bool check_this(cyg_assert_class_zeal zeal = cyg_quick) const;
CYGDBG_DECLARE_MEMLEAK_COUNTER();
protected:
CdlConflictBody(CdlTransaction, CdlNode, CdlProperty, bool /* structural */);
// The destructor gets accessed from inside the friend transaction class,
// either during a clear_conflict() or during a transaction commit.
virtual ~CdlConflictBody();
// All conflicts are associated with a node and a property.
// This information will be useful to derived classes'
// implementations of get_explanation()
CdlNode node;
CdlProperty property;
private:
// Attempt to resolve a conflict in a sub-transaction
// This is invoked from inside the transaction resolve code.
// There are additional exported interfaces inside and outside
// the transaction class.
virtual bool inner_resolve(CdlTransaction, int);
// Keep track of the transaction in which this conflict was created.
// The field is cleared at the end of a transaction.
CdlTransaction transaction;
// Usually the derived class will decide whether or not
// this conflict is structural in nature, but the data
// needs to be available at base constructor time so
// a virtual function is not appropriate.
bool structural;
// Solution support
bool no_solution;
std::vector<std::pair<CdlValuable, CdlValue> > solution;
std::set<CdlValuable> solution_references;
void update_solution_validity(CdlValuable);
// Users may disable a conflict. Usually they will have to
// supply a reason for this.
bool enabled;
std::string reason;
enum {
CdlConflictBody_Invalid = 0,
CdlConflictBody_Magic = 0x073e8853
} cdlconflictbody_cookie;
// Illegal operations. Conflicts always live on the heap.
CdlConflictBody();
CdlConflictBody(const CdlConflictBody&);
CdlConflictBody& operator=(const CdlConflictBody&);
};
// ----------------------------------------------------------------------------
// An unresolved conflict means that there is a reference in some
// property to an entity that is not yet in the current configuration.
// The class provides convenient access to the name of the unresolved
// entity.
class CdlConflict_UnresolvedBody : public CdlConflictBody {
friend class CdlTest;
public:
static void make(CdlTransaction, CdlNode, CdlProperty, std::string);
std::string get_target_name() const;
std::string get_explanation() const;
static bool test(CdlConflict);
bool check_this(cyg_assert_class_zeal = cyg_quick) const;
CYGDBG_DECLARE_MEMLEAK_COUNTER();
protected:
private:
virtual ~CdlConflict_UnresolvedBody();
CdlConflict_UnresolvedBody(CdlTransaction, CdlNode, CdlProperty, std::string);
std::string target_name;
enum {
CdlConflict_UnresolvedBody_Invalid = 0,
CdlConflict_UnresolvedBody_Magic = 0x1b24bb8a
} cdlconflict_unresolvedbody_cookie;
CdlConflict_UnresolvedBody();
CdlConflict_UnresolvedBody(const CdlConflict_UnresolvedBody&);
CdlConflict_UnresolvedBody& operator=(const CdlConflict_UnresolvedBody&);
};
// ----------------------------------------------------------------------------
// An illegal value can be caused because of a number of properties:
// legal_values, check_proc, entry_proc, ... In the case of the latter
// the Tcl code should provide text explaining why the value is
// illegal.
class CdlConflict_IllegalValueBody : public CdlConflictBody {
friend class CdlTest;
public:
static void make(CdlTransaction, CdlNode, CdlProperty);
bool resolution_implemented() const;
std::string get_explanation() const;
void set_explanation(std::string);
static bool test(CdlConflict);
bool check_this(cyg_assert_class_zeal = cyg_quick) const;
CYGDBG_DECLARE_MEMLEAK_COUNTER();
protected:
private:
virtual ~CdlConflict_IllegalValueBody();
bool inner_resolve(CdlTransaction, int);
CdlConflict_IllegalValueBody(CdlTransaction, CdlNode, CdlProperty);
std::string explanation;
enum {
CdlConflict_IllegalValueBody_Invalid = 0,
CdlConflict_IllegalValueBody_Magic = 0x4fb27ed1
} cdlconflict_illegalvaluebody_cookie;
CdlConflict_IllegalValueBody();
CdlConflict_IllegalValueBody(const CdlConflict_IllegalValueBody&);
CdlConflict_IllegalValueBody& operator=(const CdlConflict_IllegalValueBody&);
};
// ----------------------------------------------------------------------------
// There are times when expression evaluation will fail, e.g. because of
// a division by zero. The explanation is supplied by the evaluation code.
class CdlConflict_EvalExceptionBody : public CdlConflictBody {
friend class CdlTest;
public:
static void make(CdlTransaction, CdlNode, CdlProperty, std::string);
std::string get_explanation() const;
void set_explanation(std::string); // mainly for internal use
static bool test(CdlConflict);
bool check_this(cyg_assert_class_zeal = cyg_quick) const;
CYGDBG_DECLARE_MEMLEAK_COUNTER();
protected:
private:
virtual ~CdlConflict_EvalExceptionBody();
CdlConflict_EvalExceptionBody(CdlTransaction, CdlNode, CdlProperty, std::string);
std::string explanation;
enum {
CdlConflict_EvalExceptionBody_Invalid = 0,
CdlConflict_EvalExceptionBody_Magic = 0x7e64bc41
} cdlconflict_evalexceptionbody_cookie;
};
// ----------------------------------------------------------------------------
// A goal expression evaluates to false. Producing sensible diagnostics
// depends on a detailed understanding of goal expressions, which will
// have to wait until the inference engine comes along.
class CdlConflict_RequiresBody : public CdlConflictBody {
friend class CdlTest;
public:
static void make(CdlTransaction, CdlNode, CdlProperty);
bool resolution_implemented() const;
std::string get_explanation() const;
static bool test(CdlConflict);
bool check_this(cyg_assert_class_zeal = cyg_quick) const;
CYGDBG_DECLARE_MEMLEAK_COUNTER();
protected:
private:
virtual ~CdlConflict_RequiresBody();
bool inner_resolve(CdlTransaction, int);
CdlConflict_RequiresBody(CdlTransaction, CdlNode, CdlProperty);
enum {
CdlConflict_RequiresBody_Invalid = 0,
CdlConflict_RequiresBody_Magic = 0x78436331
} cdlconflict_requiresbody_cookie;
};
// ----------------------------------------------------------------------------
// There is an unusual problem in the configuration data somewhere.
// For example, a parent property can be resolved but the target is
// not a container. There is not a lot that the user can do about
// problems like this, apart from complaining to the component vendor,
// but the problem should not be ignored either.
class CdlConflict_DataBody : public CdlConflictBody {
friend class CdlTest;
public:
static void make(CdlTransaction, CdlNode, CdlProperty, std::string);
std::string get_explanation() const;
static bool test(CdlConflict);
bool check_this(cyg_assert_class_zeal = cyg_quick) const;
CYGDBG_DECLARE_MEMLEAK_COUNTER();
protected:
private:
virtual ~CdlConflict_DataBody();
CdlConflict_DataBody(CdlTransaction, CdlNode, CdlProperty, std::string);
std::string message;
enum {
CdlConflict_DataBody_Invalid = 0,
CdlConflict_DataBody_Magic = 0x2cec7ad8
} cdlconflict_databody_cookie;
};
//}}}
//{{{ CdlProperty class and derived classes
//{{{ Description
// ---------------------------------------------------------------------------
// There are many different kinds of property. An alias property contains
// a simple string. A check_proc property contains a fragment of Tcl code
// which can be represented internally as a string, as bytecodes, or both.
// A requires property contains a goal expression. ...
//
// The implementation involves a base class CdlProperty and various
// derived classes such as CdlProperty_StringBody and
// CdlProperty_ExpressionBody.
//
// New CdlProperty objects get created only when reading in CDL scripts,
// while executing commands like alias and requires. These commands are
// implemented as C++ functions hooked into the TCL interpreter. The
// property arguments are available as an argc/argv pair. Each command
// will parse and validate the arguments and then invoke an appropriate
// constructor.
//}}}
//{{{ CdlPropertyId_xxx
// ----------------------------------------------------------------------------
// Properties are identified by strings rather than by an enum or anything
// like that. A string-based approach allows new properties to be added at
// any time without invalidating an existing enum, complicating switch()
// statements, etc. There are some performance issues but these are
// manageable.
//
// A disadvantage of using strings is that there is a problem with
// typos. Mistyping something like CdlPropertyId_Compile will generally
// result in a compile-time failure. Mistyping "Complie" will cause
// strange behaviour at run-time and is hard to track down.
//
// A compromise solution is to have #define'd string constants.
#define CdlPropertyId_ActiveIf "ActiveIf"
#define CdlPropertyId_BuildProc "BuildProc"
#define CdlPropertyId_Calculated "Calculated"
#define CdlPropertyId_CancelProc "CancelProc"
#define CdlPropertyId_CheckProc "CheckProc"
#define CdlPropertyId_Compile "Compile"
#define CdlPropertyId_ConfirmProc "ConfirmProc"
#define CdlPropertyId_DecorationProc "DecorationProc"
#define CdlPropertyId_DefaultValue "DefaultValue"
#define CdlPropertyId_Define "Define"
#define CdlPropertyId_DefineHeader "DefineHeader"
#define CdlPropertyId_DefineProc "DefineProc"
#define CdlPropertyId_Description "Description"
#define CdlPropertyId_Dialog "Dialog"
#define CdlPropertyId_Display "Display"
#define CdlPropertyId_DisplayProc "DisplayProc"
#define CdlPropertyId_Doc "Doc"
#define CdlPropertyId_EntryProc "EntryProc"
#define CdlPropertyId_Flavor "Flavor"
#define CdlPropertyId_DefineFormat "DefineFormat"
#define CdlPropertyId_Group "Group"
#define CdlPropertyId_Hardware "Hardware"
#define CdlPropertyId_IfDefine "IfDefine"
#define CdlPropertyId_Implements "Implements"
#define CdlPropertyId_IncludeDir "IncludeDir"
#define CdlPropertyId_IncludeFiles "IncludeFiles"
#define CdlPropertyId_InitProc "InitProc"
#define CdlPropertyId_InstallProc "InstallProc"
#define CdlPropertyId_LegalValues "LegalValues"
#define CdlPropertyId_Library "Library"
#define CdlPropertyId_LicenseProc "LicenseProc"
#define CdlPropertyId_Make "Make"
#define CdlPropertyId_Makefile "Makefile"
#define CdlPropertyId_MakeObject "MakeObject"
#define CdlPropertyId_NoDefine "NoDefine"
#define CdlPropertyId_Object "Object"
#define CdlPropertyId_Parent "Parent"
#define CdlPropertyId_Requires "Requires"
#define CdlPropertyId_Screen "Screen"
#define CdlPropertyId_Script "Script"
#define CdlPropertyId_UpdateProc "UpdateProc"
#define CdlPropertyId_Wizard "Wizard"
//}}}
//{{{ Base class
// ----------------------------------------------------------------------------
// The base class is never used directly. Instead the appropriate derived
// objects are instantiated and when appropriate it will be necessary to
// do a dynamic cast from a CdlProperty to e.g. a CdlProperty_String.
class CdlPropertyBody {
friend class CdlTest;
public:
// The destructor is public, to avoid possible problems with STL.
virtual ~CdlPropertyBody();
// These routines provide access to the basic data.
std::string get_property_name() const;
// Get hold of the arguments that were present in the original data.
int get_argc() const;
bool has_option(std::string) const;
std::string get_option(std::string) const;
const std::vector<std::string>& get_argv() const;
const std::vector<std::pair<std::string,std::string> >& get_options() const;
// Resolve any references, or generate/update appropriate conflict
// objects. The default implementation is a no-op because not all
// properties involve references.
virtual void update(CdlTransaction, CdlNode /* source */, CdlNode /* dest */, CdlUpdate);
bool check_this(cyg_assert_class_zeal = cyg_quick) const;
CYGDBG_DECLARE_MEMLEAK_COUNTER();
protected:
// The legal constructor can only get invoked from a derived class
// constructor. The first argument identifies the property, e.g.
// CdlPropertyId_Doc (which is just #define'd to the string
// "doc").
//
// The argc and argv fields provide access to the original
// data in the command that resulted in the property being
// constructed. Often but not always argv[0] will be the same as
// the property id. The argv information is stored mainly for
// diagnostics purposes, it may be removed in future to avoid
// wasting memory.
//
// The options field is the result of parsing options such
// as -library=libextras.a. It consists of a vector of
// <name/value> pairs, and is usually obtained via
// CdlParse::parse_options().
CdlPropertyBody(CdlNode, std::string, int argc, const char* argv[], std::vector<std::pair<std::string,std::string> >&);
private:
// This string indicates the command used to define this property,
// e.g. "doc" or "define_proc". It is provided to the constructor.
std::string name;
// All property data comes out of a file and gets rid via a
// Tcl interpreter. The raw file data is stored with the property,
// mainly for diagnostics purposes.
std::vector<std::string> argv;
std::vector<std::pair<std::string, std::string> > options;
// The usual set of illegal operations.
CdlPropertyBody();
CdlPropertyBody(const CdlPropertyBody&);
CdlPropertyBody& operator=(const CdlPropertyBody&);
enum {
CdlPropertyBody_Invalid = 0,
CdlPropertyBody_Magic = 0x60dd58f4
} cdlpropertybody_cookie;
};
//}}}
//{{{ CdlProperty_Minimal
// ----------------------------------------------------------------------------
// This class is used for properties that are simple flags, e.g. no_define.
// There should be no additional data associated with such properties.
class CdlProperty_MinimalBody : public CdlPropertyBody {
friend class CdlTest;
public:
static CdlProperty_Minimal make(CdlNode, std::string, int, const char*[], std::vector<std::pair<std::string,std::string> >&);
virtual ~CdlProperty_MinimalBody( );
bool check_this( cyg_assert_class_zeal = cyg_quick ) const;
CYGDBG_DECLARE_MEMLEAK_COUNTER();
protected:
private:
typedef CdlPropertyBody inherited;
CdlProperty_MinimalBody(CdlNode, std::string, int, const char*[], std::vector<std::pair<std::string,std::string> >&);
enum {
CdlProperty_MinimalBody_Invalid = 0,
CdlProperty_MinimalBody_Magic = 0x25625b8c
} cdlproperty_minimalbody_cookie;
CdlProperty_MinimalBody();
CdlProperty_MinimalBody(const CdlProperty_MinimalBody&);
CdlProperty_MinimalBody& operator=(const CdlProperty_MinimalBody&);
};
//}}}
//{{{ CdlProperty_String
// ----------------------------------------------------------------------------
// A string property contains a single piece of additional data in the form
// of a string.
class CdlProperty_StringBody : public CdlPropertyBody {
friend class CdlTest;
public:
static CdlProperty_String make(CdlNode, std::string, std::string, int, const char*[],
std::vector<std::pair<std::string,std::string> >&);
virtual ~CdlProperty_StringBody();
std::string get_string(void) const;
bool check_this(cyg_assert_class_zeal = cyg_quick) const;
CYGDBG_DECLARE_MEMLEAK_COUNTER();
protected:
private:
typedef CdlPropertyBody inherited;
CdlProperty_StringBody(CdlNode, std::string /* id */, std::string /* data */, int, const char*[],
std::vector<std::pair<std::string,std::string> >&);
std::string data;
enum {
CdlProperty_StringBody_Invalid = 0,
CdlProperty_StringBody_Magic = 0x78d1ca94
} cdlproperty_stringbody_cookie;
// The only legal constructor supplies all the data.
CdlProperty_StringBody();
CdlProperty_StringBody(const CdlProperty_StringBody&);
CdlProperty_StringBody& operator=(const CdlProperty_StringBody&);
};
//}}}
//{{{ CdlProperty_TclCode
// ----------------------------------------------------------------------------
// A TclCode property is currently equivalent to a string property. In
// future this may change to allow the byte-compiled versions of the
// script to be stored.
//
// One of the properties, "screen" inside a cdl_wizard, also takes
// an integer. Rather than create yet another class, this is handled
// by a separate constructor.
class CdlProperty_TclCodeBody : public CdlPropertyBody {
friend class CdlTest;
public:
static CdlProperty_TclCode make(CdlNode, std::string, cdl_tcl_code, int, const char*[],
std::vector<std::pair<std::string,std::string> >&);
static CdlProperty_TclCode make(CdlNode, std::string, cdl_int, cdl_tcl_code, int, const char*[],
std::vector<std::pair<std::string,std::string> >&);
virtual ~CdlProperty_TclCodeBody();
cdl_int get_number(void) const;
const cdl_tcl_code& get_code(void) const;
bool check_this(cyg_assert_class_zeal = cyg_quick) const;
CYGDBG_DECLARE_MEMLEAK_COUNTER();
private:
typedef CdlPropertyBody inherited;
CdlProperty_TclCodeBody(CdlNode, std::string, cdl_int, cdl_tcl_code, int, const char*[],
std::vector<std::pair<std::string,std::string> >&);
cdl_int number;
cdl_tcl_code code;
enum {
CdlProperty_TclCodeBody_Invalid = 0,
CdlProperty_TclCodeBody_Magic = 0x7b14d4e5
} cdlproperty_tclcodebody_cookie;
CdlProperty_TclCodeBody();
CdlProperty_TclCodeBody(const CdlProperty_TclCodeBody&);
CdlProperty_TclCodeBody& operator=(const CdlProperty_TclCodeBody&);
};
//}}}
//{{{ CdlProperty_StringVector
// ----------------------------------------------------------------------------
// This is used for multiple constant strings, as opposed to a list
// expression which requires evaluation. One example is a list
// of aliases.
class CdlProperty_StringVectorBody : public CdlPropertyBody {
friend class CdlTest;
public:
static CdlProperty_StringVector make(CdlNode, std::string, const std::vector<std::string>&, int, const char*[],
std::vector<std::pair<std::string,std::string> >&);
virtual ~CdlProperty_StringVectorBody();
const std::vector<std::string>& get_strings() const;
std::string get_first_string() const;
unsigned int get_number_of_strings() const;
std::string get_string(unsigned int) const;
bool check_this(cyg_assert_class_zeal zeal = cyg_quick) const;
CYGDBG_DECLARE_MEMLEAK_COUNTER();
private:
typedef CdlPropertyBody inherited;
CdlProperty_StringVectorBody(CdlNode, std::string, const std::vector<std::string>&, int, const char*[],
std::vector<std::pair<std::string,std::string> >&);
std::vector<std::string> data;
enum {
CdlProperty_StringVectorBody_Invalid = 0,
CdlProperty_StringVectorBody_Magic = 0x4ed039f3
} cdlproperty_stringvectorbody_cookie;
CdlProperty_StringVectorBody();
CdlProperty_StringVectorBody(const CdlProperty_StringVectorBody&);
CdlProperty_StringVectorBody& operator=(const CdlProperty_StringVectorBody&);
};
//}}}
//{{{ CdlProperty_Reference
// ----------------------------------------------------------------------------
// This is used for properties such as wizard and dialog, where the data
// identifies some other entity in the system. The class is both a property
// and a reference object. Most of the desired functionality is provided by
// inheritance from CdlReference.
class CdlProperty_ReferenceBody : public CdlPropertyBody, public CdlReference {
friend class CdlTest;
public:
static CdlProperty_Reference make(CdlNode, std::string /* id */, std::string /* destination */,
CdlUpdateHandler, int, const char*[],
std::vector<std::pair<std::string,std::string> >&);
virtual ~CdlProperty_ReferenceBody();
void update(CdlTransaction, CdlNode, CdlNode, CdlUpdate);
bool check_this(cyg_assert_class_zeal = cyg_quick) const;
CYGDBG_DECLARE_MEMLEAK_COUNTER();
private:
typedef CdlPropertyBody inherited_property;
typedef CdlReference inherited_reference;
CdlUpdateHandler update_handler;
CdlProperty_ReferenceBody(CdlNode, std::string /* id */, std::string /* destination */, CdlUpdateHandler, int, const char*[],
std::vector<std::pair<std::string,std::string> >&);
enum {
CdlProperty_ReferenceBody_Invalid = 0,
CdlProperty_ReferenceBody_Magic = 0x78100339
} cdlproperty_referencebody_cookie;
CdlProperty_ReferenceBody();
CdlProperty_ReferenceBody(const CdlProperty_ReferenceBody&);
CdlProperty_ReferenceBody& operator=(const CdlProperty_Reference&);
};
//}}}
//{{{ CdlProperty_Expression
// ----------------------------------------------------------------------------
// An expression property simply inherits its functionality from the basic
// property class and from the expression class.
class CdlProperty_ExpressionBody : public CdlPropertyBody, public CdlExpressionBody {
friend class CdlTest;
public:
static CdlProperty_Expression make(CdlNode, std::string, CdlExpression, CdlUpdateHandler, int, const char*[],
std::vector<std::pair<std::string,std::string> >&);
virtual ~CdlProperty_ExpressionBody();
void update(CdlTransaction, CdlNode, CdlNode, CdlUpdate);
bool check_this(cyg_assert_class_zeal = cyg_quick) const;
CYGDBG_DECLARE_MEMLEAK_COUNTER();
private:
typedef CdlPropertyBody inherited_property;
typedef CdlExpressionBody inherited_expression;
CdlProperty_ExpressionBody(CdlNode, std::string, CdlExpression, CdlUpdateHandler, int, const char*[],
std::vector<std::pair<std::string,std::string> >&);
CdlUpdateHandler update_handler;
enum {
CdlProperty_ExpressionBody_Invalid = 0,
CdlProperty_ExpressionBody_Magic = 0x05fb4056
} cdlproperty_expressionbody_cookie;
CdlProperty_ExpressionBody();
CdlProperty_ExpressionBody(const CdlProperty_ExpressionBody&);
CdlProperty_ExpressionBody& operator=(const CdlProperty_ExpressionBody&);
};
//}}}
//{{{ CdlProperty_ListExpression
// ----------------------------------------------------------------------------
// Similarly a list property simply inherits from property and from
// list expressions.
class CdlProperty_ListExpressionBody : public CdlPropertyBody, public CdlListExpressionBody {
friend class CdlTest;
public:
static CdlProperty_ListExpression make(CdlNode, std::string, CdlListExpression, CdlUpdateHandler, int, const char*[],
std::vector<std::pair<std::string,std::string> >&);
virtual ~CdlProperty_ListExpressionBody();
void update(CdlTransaction, CdlNode, CdlNode, CdlUpdate);
bool check_this(cyg_assert_class_zeal = cyg_quick) const;
CYGDBG_DECLARE_MEMLEAK_COUNTER();
private:
typedef CdlPropertyBody inherited_property;
typedef CdlListExpressionBody inherited_expression;
CdlProperty_ListExpressionBody(CdlNode, std::string, CdlListExpression, CdlUpdateHandler, int, const char*[],
std::vector<std::pair<std::string,std::string> >&);
CdlUpdateHandler update_handler;
enum {
CdlProperty_ListExpressionBody_Invalid = 0,
CdlProperty_ListExpressionBody_Magic = 0x6b0136f5
} cdlproperty_listexpressionbody_cookie;
CdlProperty_ListExpressionBody();
CdlProperty_ListExpressionBody(const CdlProperty_ListExpressionBody&);
CdlProperty_ListExpressionBody& operator=(const CdlProperty_ListExpressionBody&);
};
//}}}
//{{{ CdlProperty_GoalExpression
// ----------------------------------------------------------------------------
// And a goal property inherits from property and from goal expressions.
class CdlProperty_GoalExpressionBody : public CdlPropertyBody, public CdlGoalExpressionBody {
friend class CdlTest;
public:
static CdlProperty_GoalExpression make(CdlNode, std::string, CdlGoalExpression, CdlUpdateHandler, int, const char*[],
std::vector<std::pair<std::string,std::string> >&);
virtual ~CdlProperty_GoalExpressionBody();
void update(CdlTransaction, CdlNode, CdlNode, CdlUpdate);
bool check_this(cyg_assert_class_zeal = cyg_quick) const;
CYGDBG_DECLARE_MEMLEAK_COUNTER();
private:
typedef CdlPropertyBody inherited_property;
typedef CdlGoalExpressionBody inherited_expression;
CdlProperty_GoalExpressionBody(CdlNode, std::string, CdlGoalExpression, CdlUpdateHandler, int, const char*[],
std::vector<std::pair<std::string,std::string> >&);
CdlUpdateHandler update_handler;
enum {
CdlProperty_GoalExpressionBody_Invalid = 0,
CdlProperty_GoalExpressionBody_Magic = 0x08b2b31e
} cdlproperty_goalexpressionbody_cookie;
CdlProperty_GoalExpressionBody();
CdlProperty_GoalExpressionBody(const CdlProperty_GoalExpressionBody&);
CdlProperty_GoalExpressionBody& operator=(const CdlProperty_GoalExpressionBody&);
};
//}}}
//}}}
//{{{ CdlParse class
// ----------------------------------------------------------------------------
// This is another utility class for collecting together parsing-related
// functions.
//
// Note that this is only a utility class. When libcdl is used for parsing
// things not related to software configuration the new functionality
// does not have to reside inside the CdlParse class, but it may be
// possible to re-use some of the functionality in that class.
class CdlParse {
public:
// Utility routines.
static std::string get_tcl_cmd_name(std::string);
static std::string concatenate_argv(int, const char*[], int);
static int parse_options(CdlInterpreter, std::string /* diag_prefix */, char** /* options */,
int /* argc */, const char*[] /* argv */, int /* start_index */,
std::vector<std::pair<std::string,std::string> >& /* result */);
static std::string construct_diagnostic(CdlInterpreter, std::string /* classification */,
std::string /* sub-identifier */, std::string /* message */);
static void report_error(CdlInterpreter, std::string /* sub-identifier */, std::string /* message */);
static void report_warning(CdlInterpreter, std::string /* sub-identifier */, std::string /* message */);
static void clear_error_count(CdlInterpreter);
static int get_error_count(CdlInterpreter);
static void incr_error_count(CdlInterpreter, int=1);
static std::string get_expression_error_location(void);
// Support for Tcl's "unknown" command
static int unknown_command(CdlInterpreter, int, const char*[]);
// Property-related utilities
static void report_property_parse_error(CdlInterpreter, std::string, std::string);
static void report_property_parse_error(CdlInterpreter, CdlProperty, std::string);
static void report_property_parse_warning(CdlInterpreter, std::string, std::string);
static void report_property_parse_warning(CdlInterpreter, CdlProperty, std::string);
// Utility parsing routines
static int parse_minimal_property(CdlInterpreter, int, const char*[], std::string,
char**, void (*)(CdlInterpreter, CdlProperty_Minimal));
static int parse_string_property(CdlInterpreter, int, const char*[], std::string,
char**, void (*)(CdlInterpreter, CdlProperty_String));
static int parse_tclcode_property(CdlInterpreter, int, const char*[], std::string,
char**, void (*)(CdlInterpreter, CdlProperty_TclCode));
static int parse_stringvector_property(CdlInterpreter, int, const char*[], std::string,
char**, void (*)(CdlInterpreter, CdlProperty_StringVector),
bool /* allow_empty */ = false);
static int parse_reference_property(CdlInterpreter, int, const char*[], std::string,
char**, void (*)(CdlInterpreter, CdlProperty_Reference),
bool /* allow_empty */,
CdlUpdateHandler);
static int parse_expression_property(CdlInterpreter, int, const char*[], std::string,
char **, void (*)(CdlInterpreter, CdlProperty_Expression),
CdlUpdateHandler);
static int parse_listexpression_property(CdlInterpreter, int, const char*[], std::string,
char **, void (*)(CdlInterpreter, CdlProperty_ListExpression),
CdlUpdateHandler);
static int parse_goalexpression_property(CdlInterpreter, int, const char*[], std::string,
char **, void (*)(CdlInterpreter, CdlProperty_GoalExpression),
CdlUpdateHandler);
};
//}}}
//{{{ CdlNode
// ----------------------------------------------------------------------------
// A node object has a name and lives in a hierarchy. Each node keeps
// track of the toplevel and owner. The memory overheads are
// relatively small compared with the performance gains when such
// information is needed.
//
// A node object also has a vector of properties, and can be referred to
// by properties in other nodes. Some of the properties may result in
// conflicts.
class CdlNodeBody {
friend class CdlTest;
// Adding and removing nodes from the hierarchy is done
// by CdlToplevel members.
friend class CdlToplevelBody;
// CdlLoadable must be able to access the destructor
friend class CdlLoadableBody;
// It is intended that CdlProperties will also add and remove themselves
friend class CdlPropertyBody;
// CdlReference bind and unbind operations need access to
// the referrers vector. So does CdlTransaction::commit()
friend class CdlReference;
friend class CdlTransactionBody;
public:
// Basic information.
std::string get_name() const;
CdlContainer get_parent() const;
CdlLoadable get_owner() const;
CdlToplevel get_toplevel() const;
// Propagation support. Some updates such as active/inactive changes
// get applied to nodes as well as to properties. Note that because
// of multiple inheritance this virtual call can get confusing.
virtual void update(CdlTransaction, CdlUpdate);
// Is this node active or not? The is_active() call refers
// to the global state, things may be different inside a
// transaction.
bool is_active() const;
bool is_active(CdlTransaction transaction);
// Generally nodes become active when the parent becomes
// active and enabled. Some derived classes may impose
// additional restrictions, for example because of
// active_if constraints. This routine can be used
// to check whether or not a node should become active.
virtual bool test_active(CdlTransaction);
// Provide access to the various properties. Currently all this
// information is publicly available.
const std::vector<CdlProperty>& get_properties() const;
CdlProperty get_property(std::string) const;
void get_properties(std::string, std::vector<CdlProperty>&) const;
std::vector<CdlProperty> get_properties(std::string) const;
bool has_property(std::string) const;
int count_properties(std::string) const;
// Provide access to the various global conflicts. More
// commonly conflicts are accessed on a per-transaction basis.
void get_conflicts(std::vector<CdlConflict>&) const;
void get_conflicts(bool (*)(CdlConflict), std::vector<CdlConflict>&) const;
void get_structural_conflicts(std::vector<CdlConflict>&) const;
void get_structural_conflicts(bool (*)(CdlConflict), std::vector<CdlConflict>&) const;
// Provide access to all the referrers. This may not get used very
// much outside the library itself.
const std::vector<CdlReferrer>& get_referrers() const;
// Add property parsers and validation code appropriate for a
// node. Currently this is a no-op, there are no properties
// associated with every node, but this may change in future e.g.
// for diagnostics purposes.
static void add_property_parsers(std::vector<CdlInterpreterCommandEntry>& parsers);
void check_properties(CdlInterpreter);
// Persistence support. The final classes such as cdl_option
// should provide implementations of these functions. The
// base function takes care of data that was present in an
// original save file but which was not recognised.
//
// Configuration save files are Tcl scripts, so it seems
// appropriate to handle the I/O via the Tcl library and
// to have a TCL interpreter available.
virtual void save(CdlInterpreter, Tcl_Channel, int, bool);
bool has_additional_savefile_information() const;
// Mainly for diagnostics code, what is the actual name for this
// type of CDL object? This should be in terms of CDL data, e.g.
// "package" or "component", rather than in implementation terms
// such as "CdlPackageBody".
virtual std::string get_class_name() const;
bool check_this(cyg_assert_class_zeal = cyg_quick) const;
CYGDBG_DECLARE_MEMLEAK_COUNTER();
protected:
// CdlNodeBodies are only instantiated by derived classes.
// They must always have a name. They need not be placed
// in the hierarchy immediately, that can wait until
// later.
CdlNodeBody(std::string);
// A dummy constructor is needed because of the virtual
// inheritance.
CdlNodeBody();
// Nodes cannot be destroyed directly by application code,
// only by higher-level library functions such as unload_package()
virtual ~CdlNodeBody();
// Updating the name is rarely required, but is useful for savefiles.
void set_name(std::string);
// Is the node currently active? This applies to the global state
// only, not per-transaction state. Some derived classes may want
// to override the default value
bool active;
private:
// The basic data. The name is known during construction.
// The other three fields get updated by e.g. CdlToplevel::add_node();
std::string name;
CdlContainer parent;
CdlLoadable owner;
CdlToplevel toplevel;
// This is used by remove_node_from_toplevel()/add_node_to_toplevel()
// to allow the latter to exactly reverse the former
int remove_node_container_position;
// Properties normally only get added during the parsing process,
// and only get removed when the object itself is destroyed.
// A vector is the obvious implementation.
std::vector<CdlProperty> properties;
// Currently a vector of referrers is used. This vector is subject
// to change when packages get loaded and unloaded, possibly a
// list would be better.
std::vector<CdlReferrer> referrers;
// Savefiles may contain information that is not recognised by the
// current library, especially because of savefile hooks which
// allow e.g. the configuration tool to store its own information
// in save files. This information must not be lost, even if you are
// e.g. mixing command line and GUI tools. This vector holds
// the savefile information so that it can be put in the next
// savefile.
std::vector<std::string> unsupported_savefile_strings;
enum {
CdlNodeBody_Invalid = 0,
CdlNodeBody_Magic = 0x309595b5
} cdlnodebody_cookie;
// Illegal operations
CdlNodeBody(const CdlNodeBody&);
CdlNodeBody& operator=(const CdlNodeBody&);
};
//}}}
//{{{ CdlContainer
// ----------------------------------------------------------------------------
// A container is a node that can contain other nodes.
class CdlContainerBody : virtual public CdlNodeBody {
friend class Cdltest;
// Allow CdlNode::check_this() access to the internals
friend class CdlNodeBody;
// Adding a node to the hierarchy is done by a CdlToplevel member.
// Ditto for removing.
friend class CdlToplevelBody;
// Deleting a container can happen inside CdlToplevel and CdlLoadable
friend class CdlLoadableBody;
public:
const std::vector<CdlNode>& get_contents() const;
bool contains(CdlConstNode, bool /* recurse */ = false) const;
bool contains(const std::string, bool /* recurse */ = false) const;
CdlNode find_node(const std::string, bool /* recurse */ = false) const;
// Propagation support. Some updates such as active/inactive changes
// get applied to nodes as well as to properties.
virtual void update(CdlTransaction, CdlUpdate);
// Persistence support.
virtual void save(CdlInterpreter, Tcl_Channel, int, bool);
virtual std::string get_class_name() const;
bool check_this(cyg_assert_class_zeal = cyg_quick) const;
CYGDBG_DECLARE_MEMLEAK_COUNTER();
protected:
// Containers cannot be destroyed explicitly, only via higher-level
// code such as unload_package();
virtual ~CdlContainerBody();
CdlContainerBody();
// Special constructor needed for internal use.
CdlContainerBody(std::string);
// The CdlToplevel class needs access to its own contents.
std::vector<CdlNode> contents;
private:
enum {
CdlContainerBody_Invalid = 0,
CdlContainerBody_Magic = 0x543c5f1d
} cdlcontainerbody_cookie;
// Illegal operations
CdlContainerBody(const CdlContainerBody&);
CdlContainerBody& operator=(const CdlContainerBody&);
};
//}}}
//{{{ CdlLoadable
// ----------------------------------------------------------------------------
// A loadable object is a container that gets loaded or unloaded
// atomically from a toplevel. The key difference is that a loadable
// keeps track of all nodes that were loaded as part of this
// operation, thus allowing unload operations to happen safely even if
// nodes get re-parented all over the hierarchy. In addition, there is
// a slave interpreter associated with every loadable.
class CdlLoadableBody : virtual public CdlContainerBody {
friend class CdlTest;
// Allow CdlNode::check_this() access to the internals
friend class CdlNodeBody;
// Adding nodes to the hierarchy is done by a toplevel member
friend class CdlToplevelBody;
public:
virtual ~CdlLoadableBody();
const std::vector<CdlNode>& get_owned() const;
bool owns(CdlConstNode) const;
CdlInterpreter get_interpreter() const;
std::string get_directory() const;
// Some properties such as doc and compile reference filenames.
// A search facility is useful.
virtual std::string find_relative_file(std::string /* filename */, std::string /* directory */ = "") const;
virtual std::string find_absolute_file(std::string, std::string, bool /* allow_urls */ = false) const;
virtual bool has_subdirectory(std::string) const;
// These support load/unload operations inside transactions
// They are static members because some of them will want
// to delete the loadable.
static void transaction_commit_load(CdlTransaction, CdlLoadable);
static void transaction_cancel_load(CdlTransaction, CdlLoadable);
static void transaction_commit_unload(CdlTransaction, CdlLoadable);
static void transaction_cancel_unload(CdlTransaction, CdlLoadable);
// Binding and unbinding of properties. This involves processing
// the various properties, calculating default values, etc.
void bind(CdlTransaction);
void unbind(CdlTransaction);
virtual std::string get_class_name() const;
bool check_this(cyg_assert_class_zeal = cyg_quick) const;
CYGDBG_DECLARE_MEMLEAK_COUNTER();
protected:
CdlLoadableBody(CdlToplevel, std::string /* directory */);
// Needed by derived classes, but not actually used.
CdlLoadableBody();
private:
std::vector<CdlNode> owned;
CdlInterpreter interp;
std::string directory;
// Used by add/remove_node_from_toplevel()
int remove_node_loadables_position;
enum {
CdlLoadableBody_Invalid = 0,
CdlLoadableBody_Magic = 0x488d6127
} cdlloadablebody_cookie;
// Invalid operations
CdlLoadableBody(const CdlLoadableBody&);
CdlLoadableBody& operator=(const CdlLoadableBody&);
};
//}}}
//{{{ CdlToplevel
// ----------------------------------------------------------------------------
// Toplevels are containers that live at the top of a hierarchy
// (surprise surprise). This means that they do not have a parent.
// In addition a toplevel object keeps track of all the names
// used, guaranteeing uniqueness and providing a quick lookup
// facility.
//
// Every container is also a node, so every toplevel is a node.
// Inheritance from CdlNode may seem wrong. However it achieves
// consistency, everything in the hierarchy including the toplevel
// is a node. The main disadvantage is that every toplevel now
// needs a name.
class CdlToplevelBody : virtual public CdlContainerBody {
friend class CdlTest;
// Allow CdlNode::check_this() access to the internals
friend class CdlNodeBody;
// The CdlTransaction class needs direct access to the lists
// of conflicts.
friend class CdlTransactionBody;
public:
virtual ~CdlToplevelBody();
// Updating the hierarchy. This happens a node at a time. Adding a
// node involves updating the name->node map in the toplevel,
// setting the node's parent/owner/toplevel fields, and updating
// the parent and owner containers. The owner may be 0 for special
// nodes such as the orphans container. The parent must be known,
// although it may change later on during a change_parent() call.
//
// Removing a node is more complicated, and involves a two-stage
// process. First the node is removed from the toplevel, thus
// eliminating the name->node mapping. The owner and parent fields
// are preserved at this stage (except for the loadable itself),
// and the operation may be undone if the relevant transaction
// gets cancelled. If the transaction gets committed then the
// second remove operation handles the owner and parent fields,
// just prior to the node being deleted. For convenience there
// are also per-loadable variants for some of these.
//
// change_parent() is used to support parent-properties.
// A container of 0 indicates an orphan, i.e. a parent
// property that did not or does not correspond to a
// current container.
//
// There is also a clean-up call. This gets used for interfaces
// which may alternate between belonging to a loadable and
// being auto-created.
void add_node(CdlLoadable, CdlContainer, CdlNode);
void add_node_to_toplevel(CdlNode);
void remove_node_from_toplevel(CdlNode);
static void remove_node(CdlLoadable, CdlContainer, CdlNode);
void add_loadable_to_toplevel(CdlLoadable);
void remove_loadable_from_toplevel(CdlLoadable);
void change_parent(CdlLoadable, CdlContainer /* current */, CdlContainer /* new */, CdlNode, int /* pos */ = -1);
void cleanup_orphans();
// Toplevels keep track of all the loadables, in addition to
// inheriting tree behaviour from CdlContainer. This is convenient
// for some operations like determining build information
// which must operate on a per-loadable basis.
const std::vector<CdlLoadable>& get_loadables() const;
// Name uniqueness is guaranteed. It is convenient to have an STL
// map as a lookup service.
CdlNode lookup(const std::string) const;
// There are two conflict lists associated with each toplevel. One
// is for "structural" conflicts, ones that can only be resolved
// by a fairly major change such as loading another package: a
// typical example is an unresolved parent reference. The other is
// for conflicts that can probably be resolved simply by changing
// some values. Both sets of conflicts are held as a simple list.
//
// The active vs. inactive state of a CDL entity affects the
// location of structural vs. non-structural conflicts. If an
// entity becomes inactive then structural conflicts are not
// affected, but non-structural conflicts are removed from the
// global list. If an entity's "requires" expression is not
// satisfied but the entity is inactive anyway then this is
// harmless.
const std::list<CdlConflict>& get_all_conflicts() const;
const std::list<CdlConflict>& get_all_structural_conflicts() const;
// Try to resolve some or all conflicts. Typically a new transaction
// will be created for this.
void resolve_conflicts(const std::vector<CdlConflict>&);
void resolve_all_conflicts();
// Toplevels can have descriptions provided by the user. This is
// particularly important for pre-defined templates, target
// board descriptions, etc. where the user would like some
// extra information about the template before loading it in.
// The default value is an empty string.
std::string get_description() const;
void set_description(std::string);
// Each toplevel must have an associated master Tcl interpreter.
CdlInterpreter get_interpreter() const;
// Each toplevel should also have an associated directory for
// the component repository. It is not required that all loadables
// are relative to this, but that is the default behaviour.
std::string get_directory() const;
// Each toplevel may have a single active main transaction.
// For now there is no support for concurrent transactions
// operating on a single toplevel (although nested transactions
// are allowed)
CdlTransaction get_active_transaction() const;
// Build and define operations are available for all toplevels,
// even if they are not always applicable
void get_build_info(CdlBuildInfo&);
void get_all_build_info(CdlBuildInfo&);
void generate_config_headers(std::string);
void get_config_headers(std::vector<std::string>&);
void generate_build_tree(std::string, std::string = "");
// Values can be stored in limbo. This is useful when unloading
// and reloading packages, e.g. when changing a version the
// current settings can be preserved as much as possible.
void set_limbo_value(CdlValuable);
bool has_limbo_value(std::string) const;
CdlValue get_limbo_value(std::string) const;
CdlValue get_and_remove_limbo_value(std::string);
void clear_limbo();
// Persistence support. These are commented in the source code.
void initialize_savefile_support();
static bool savefile_support_initialized();
void add_savefile_command(std::string, CdlSaveCallback, CdlInterpreterCommand);
void add_savefile_subcommand(std::string, std::string, CdlSaveCallback, CdlInterpreterCommand);
void get_savefile_commands(std::vector<CdlInterpreterCommandEntry>&);
void get_savefile_subcommands(std::string, std::vector<CdlInterpreterCommandEntry>&);
void save_command_details(CdlInterpreter, Tcl_Channel, int, bool);
static int savefile_handle_command(CdlInterpreter, int, const char*[]);
static int savefile_handle_unsupported(CdlInterpreter, int, const char*[]);
static int savefile_handle_unknown(CdlInterpreter, int, const char*[]);
void save_unsupported_commands(CdlInterpreter, Tcl_Channel, int, bool);
static cdl_int get_library_savefile_version();
static int savefile_handle_version(CdlInterpreter, int, const char*[]);
static cdl_int get_savefile_version(CdlInterpreter);
void save_conflicts(CdlInterpreter, Tcl_Channel, int, bool);
static void save_separator(CdlInterpreter, Tcl_Channel, std::string, bool);
virtual std::string get_class_name() const;
bool check_this(cyg_assert_class_zeal = cyg_quick) const;
CYGDBG_DECLARE_MEMLEAK_COUNTER();
protected:
CdlToplevelBody(CdlInterpreter, std::string);
private:
std::map<std::string,CdlNode> lookup_table;
std::vector<CdlLoadable> loadables;
std::map<std::string,CdlValue> limbo;
CdlInterpreter interp;
CdlContainer orphans;
std::string description;
std::string directory;
std::list<CdlConflict> conflicts;
std::list<CdlConflict> structural_conflicts;
// The savefile support corresponding to this application.
static cdl_int savefile_version;
static bool savefile_commands_initialized;
static std::vector<CdlSavefileCommand> savefile_commands;
static std::map<std::string,std::vector<CdlSavefileCommand> > savefile_subcommands;
// Per-toplevel support. A savefile may contain unrecognised
// commands at the toplevel of a file, as well as unrecognised
// commands in e.g. the body of a cdl_configuration command.
// The latter is handled via the CdlNode base class.
std::vector<std::string> unsupported_savefile_toplevel_strings;
std::vector<std::string> unsupported_savefile_commands;
std::map<std::string, std::vector<std::string> > unsupported_savefile_subcommands;
// Keep track of the current active transaction for this toplevel (if any)
CdlTransaction transaction;
enum {
CdlToplevelBody_Invalid = 0,
CdlToplevelBody_Magic = 0x0834666e
} cdltoplevelbody_cookie;
// Invalid operations
CdlToplevelBody(const CdlToplevelBody&);
CdlToplevelBody& operator=(const CdlToplevelBody&);
};
//}}}
//{{{ CdlUserVisible
// ----------------------------------------------------------------------------
// A user-visible object is likely to have properties such as display,
// description and doc. Many user-visible objects will have values but
// not all, for example custom dialogs are likely to have a doc
// property but they do not have a value.
class CdlUserVisibleBody : virtual public CdlNodeBody {
friend class CdlTest;
public:
virtual ~CdlUserVisibleBody();
std::string get_display() const;
std::string get_description() const;
std::string get_doc() const;
// NOTE: this will only work for absolute doc strings or for doc
// strings that are relative to the package.
std::string get_doc_url() const;
// Add property parsers and validation code appropriate for a
// user-visible object such as doc and description
static void add_property_parsers(std::vector<CdlInterpreterCommandEntry>& parsers);
void check_properties(CdlInterpreter);
static int parse_description(CdlInterpreter, int, const char*[]);
static int parse_display(CdlInterpreter, int, const char*[]);
static int parse_doc(CdlInterpreter, int, const char*[]);
// Persistence support. The save code simply outputs some comments
// corresponding to the display, doc and description properties.
virtual void save(CdlInterpreter, Tcl_Channel, int, bool);
virtual std::string get_class_name() const;
bool check_this(cyg_assert_class_zeal = cyg_quick) const;
CYGDBG_DECLARE_MEMLEAK_COUNTER();
protected:
CdlUserVisibleBody();
private:
enum {
CdlUserVisibleBody_Invalid = 0,
CdlUserVisibleBody_Magic = 0x13bbc817
} cdluservisiblebody_cookie;
// Illegal operations
CdlUserVisibleBody(const CdlUserVisibleBody&);
CdlUserVisibleBody& operator=(const CdlUserVisibleBody&);
};
//}}}
//{{{ CdlParentable
// ----------------------------------------------------------------------------
// A parentable object may have the parent property, redefining its
// position in the hierarchy.
class CdlParentableBody : virtual public CdlNodeBody {
friend class CdlTest;
public:
virtual ~CdlParentableBody();
static void add_property_parsers(std::vector<CdlInterpreterCommandEntry>& parsers);
void check_properties(CdlInterpreter);
static int parse_parent(CdlInterpreter, int, const char*[]);
static void update_handler(CdlTransaction, CdlNode, CdlProperty, CdlNode, CdlUpdate);
virtual std::string get_class_name() const;
bool check_this(cyg_assert_class_zeal = cyg_quick) const;
CYGDBG_DECLARE_MEMLEAK_COUNTER();
protected:
CdlParentableBody();
private:
// Unloads may be cancelled. To restore the previous state exactly
// it is necessary to keep track of the old position.
int change_parent_save_position;
enum {
CdlParentableBody_Invalid = 0,
CdlParentableBody_Magic = 0x40c6a077
} cdlparentablebody_cookie;
// Illegal operations
CdlParentableBody(const CdlParentableBody&);
CdlParentableBody& operator=(const CdlParentableBody&);
};
//}}}
//{{{ CdlValuable
// ----------------------------------------------------------------------------
// A valuable body has a value. Many valuables can be modified but not all.
// Some properties make a valuable object read-only. In future there is
// likely to be support for locked values as well. There is a member function
// to check whether or not a valuable object is modifiable.
//
// Relevant properties for a valuable object are:
//
// 1) flavor - readily available via CdlValue::get_flavor()
// 2) default_value - an expression
// 3) legal_values - a list expression
// 4) entry_proc - for validation purposes, in addition to legal_values
// 5) check_proc - ditto
// 6) active_if - goal expression
// 7) requires - goal expression
// 8) dialog - a custom dialog for editing this value
// 9) calculated - non-modifiable
// 10) implements - for interfaces
//
// A CdlValuable does not inherit directly from CdlValue, since it should
// not be possible to modify a Valuable directly. Instead it contains a
// CdlValue member, and provides essentially the same functions as
// a CdlValue.
class CdlValuableBody : virtual public CdlNodeBody {
friend class CdlTest;
// Transaction commit operations require direct access to the CdlValue
friend class CdlTransactionBody;
private:
CdlValue value;
public:
virtual ~CdlValuableBody();
// Accessing the current value. There are variants for the global state
// and for per-transaction operations.
const CdlValue& get_whole_value() const;
CdlValueFlavor get_flavor() const;
CdlValueFlavor get_flavor(CdlTransaction transaction) const
{ // The transaction is irrelevant, it cannot change the flavor
return this->get_flavor();
}
CdlValueSource get_source() const;
bool has_source( CdlValueSource) const;
bool is_enabled( CdlValueSource = CdlValueSource_Current) const;
std::string get_value( CdlValueSource = CdlValueSource_Current) const;
bool has_integer_value( CdlValueSource = CdlValueSource_Current) const;
cdl_int get_integer_value( CdlValueSource = CdlValueSource_Current) const;
bool has_double_value( CdlValueSource = CdlValueSource_Current) const;
double get_double_value( CdlValueSource = CdlValueSource_Current) const;
CdlSimpleValue get_simple_value( CdlValueSource = CdlValueSource_Current) const;
CdlValueSource get_source(CdlTransaction) const;
bool has_source( CdlTransaction, CdlValueSource) const;
bool is_enabled( CdlTransaction, CdlValueSource = CdlValueSource_Current) const;
std::string get_value( CdlTransaction, CdlValueSource = CdlValueSource_Current) const;
bool has_integer_value( CdlTransaction, CdlValueSource = CdlValueSource_Current) const;
cdl_int get_integer_value( CdlTransaction, CdlValueSource = CdlValueSource_Current) const;
bool has_double_value( CdlTransaction, CdlValueSource = CdlValueSource_Current) const;
double get_double_value( CdlTransaction, CdlValueSource = CdlValueSource_Current) const;
CdlSimpleValue get_simple_value( CdlTransaction, CdlValueSource = CdlValueSource_Current) const;
// -----------------------------------------------------------------
// Modify access. There are two variants of all the functions:
//
// 1) no transaction argument. A transaction will be created,
// committed, and destroyed for the change in question.
//
// 2) a transaction argument. The existing transaction will be
// updated but not committed. This allows multiple changes
// to be grouped together.
//
// There are only a handful of exported functions, but lots
// of inline variants.
void set_source(CdlValueSource);
void invalidate_source(CdlValueSource);
void set_enabled(bool, CdlValueSource);
void set_value(CdlSimpleValue&, CdlValueSource);
void set_enabled_and_value(bool, CdlSimpleValue&, CdlValueSource);
void set(CdlSimpleValue&, CdlValueSource);
void set_source(CdlTransaction, CdlValueSource);
void invalidate_source(CdlTransaction, CdlValueSource);
void set_enabled(CdlTransaction, bool, CdlValueSource);
void set_value(CdlTransaction, CdlSimpleValue&, CdlValueSource);
void set_enabled_and_value(CdlTransaction, bool, CdlSimpleValue&, CdlValueSource);
void set(CdlTransaction, CdlSimpleValue&, CdlValueSource);
void set(CdlTransaction, const CdlValue&);
void enable(CdlValueSource source)
{
set_enabled(true, source);
}
void disable(CdlValueSource source)
{
set_enabled(false, source);
}
void set_value(std::string data, CdlValueSource source)
{
CdlSimpleValue val(data);
set_value(val, source);
}
void set_integer_value(cdl_int data, CdlValueSource source)
{
CdlSimpleValue val(data);
set_value(val, source);
}
void set_double_value(double data, CdlValueSource source)
{
CdlSimpleValue val(data);
set_value(val, source);
}
void set_enabled_and_value(bool enabled, std::string data, CdlValueSource source)
{
CdlSimpleValue val(data);
set_enabled_and_value(enabled, val, source);
}
void set_enabled_and_value(bool enabled, cdl_int data, CdlValueSource source)
{
CdlSimpleValue val(data);
set_enabled_and_value(enabled, val, source);
}
void set_enabled_and_value(bool enabled, double data, CdlValueSource source)
{
CdlSimpleValue val(data);
set_enabled_and_value(enabled, val, source);
}
void enable_and_set_value(CdlSimpleValue& val, CdlValueSource source)
{
set_enabled_and_value(true, val, source);
}
void enable_and_set_value(std::string data, CdlValueSource source)
{
set_enabled_and_value(true, data, source);
}
void enable_and_set_value(cdl_int data, CdlValueSource source)
{
set_enabled_and_value(true, data, source);
}
void enable_and_set_value(double data, CdlValueSource source)
{
set_enabled_and_value(true, data, source);
}
void disable_and_set_value(CdlSimpleValue& val, CdlValueSource source)
{
set_enabled_and_value(false, val, source);
}
void disable_and_set_value(std::string data, CdlValueSource source)
{
set_enabled_and_value(false, data, source);
}
void disable_and_set_value(cdl_int data, CdlValueSource source)
{
set_enabled_and_value(false, data, source);
}
void disable_and_set_value(double data, CdlValueSource source)
{
set_enabled_and_value(false, data, source);
}
void enable(CdlTransaction transaction, CdlValueSource source)
{
set_enabled(transaction, true, source);
}
void disable(CdlTransaction transaction, CdlValueSource source)
{
set_enabled(transaction, false, source);
}
void set_value(CdlTransaction transaction, std::string data, CdlValueSource source)
{
CdlSimpleValue val(data);
set_value(transaction, val, source);
}
void set_integer_value(CdlTransaction transaction, cdl_int data, CdlValueSource source)
{
CdlSimpleValue val(data);
set_value(transaction, val, source);
}
void set_double_value(CdlTransaction transaction, double data, CdlValueSource source)
{
CdlSimpleValue val(data);
set_value(transaction, val, source);
}
void set_enabled_and_value(CdlTransaction transaction, bool enabled, std::string data, CdlValueSource source)
{
CdlSimpleValue val(data);
set_enabled_and_value(transaction, enabled, val, source);
}
void set_enabled_and_value(CdlTransaction transaction, bool enabled, cdl_int data, CdlValueSource source)
{
CdlSimpleValue val(data);
set_enabled_and_value(transaction, enabled, val, source);
}
void set_enabled_and_value(CdlTransaction transaction, bool enabled, double data, CdlValueSource source)
{
CdlSimpleValue val(data);
set_enabled_and_value(transaction, enabled, val, source);
}
void enable_and_set_value(CdlTransaction transaction, CdlSimpleValue& val, CdlValueSource source)
{
set_enabled_and_value(transaction, true, val, source);
}
void enable_and_set_value(CdlTransaction transaction, std::string data, CdlValueSource source)
{
set_enabled_and_value(transaction, true, data, source);
}
void enable_and_set_value(CdlTransaction transaction, cdl_int data, CdlValueSource source)
{
set_enabled_and_value(transaction, true, data, source);
}
void enable_and_set_value(CdlTransaction transaction, double data, CdlValueSource source)
{
set_enabled_and_value(transaction, true, data, source);
}
void disable_and_set_value(CdlTransaction transaction, CdlSimpleValue& val, CdlValueSource source)
{
set_enabled_and_value(transaction, false, val, source);
}
void disable_and_set_value(CdlTransaction transaction, std::string data, CdlValueSource source)
{
set_enabled_and_value(transaction, false, data, source);
}
void disable_and_set_value(CdlTransaction transaction, cdl_int data, CdlValueSource source)
{
set_enabled_and_value(transaction, false, data, source);
}
void disable_and_set_value(CdlTransaction transaction, double data, CdlValueSource source)
{
set_enabled_and_value(transaction, false, data, source);
}
// -----------------------------------------------------------------
virtual bool is_modifiable() const;
void get_widget_hint(CdlWidgetHint&);
// -----------------------------------------------------------------
// Propagation support. If a valuable becomes active or inactive
// because e.g. its parent is disabled then this may affect
// requires conflicts etc.
virtual void update(CdlTransaction, CdlUpdate);
virtual bool test_active(CdlTransaction);
// -----------------------------------------------------------------
// Property-related stuff.
bool has_calculated_expression() const;
bool has_default_value_expression() const;
bool has_legal_values() const;
bool has_entry_proc() const;
bool has_check_proc() const;
bool has_active_if_conditions() const;
bool has_requires_goals() const;
bool has_dialog() const;
bool has_wizard() const;
CdlProperty_Expression get_calculated_expression() const;
CdlProperty_Expression get_default_value_expression() const;
CdlProperty_ListExpression get_legal_values() const;
cdl_tcl_code get_entry_proc() const;
cdl_tcl_code get_check_proc() const;
void get_active_if_conditions(std::vector<CdlProperty_GoalExpression>&) const;
void get_requires_goals(std::vector<CdlProperty_GoalExpression>&) const;
CdlDialog get_dialog() const;
CdlWizard get_wizard() const;
void get_implemented_interfaces(std::vector<CdlInterface>&) const;
// Add property parsers and validation code appropriate for a
// valuable object such as default_value and legal_values
static void add_property_parsers(std::vector<CdlInterpreterCommandEntry>& parsers);
void check_properties(CdlInterpreter);
static int parse_active_if(CdlInterpreter, int, const char*[]);
static void active_if_update_handler(CdlTransaction, CdlNode, CdlProperty, CdlNode, CdlUpdate);
static int parse_calculated(CdlInterpreter, int, const char*[]);
static void calculated_update_handler(CdlTransaction, CdlNode, CdlProperty, CdlNode, CdlUpdate);
static int parse_check_proc(CdlInterpreter, int, const char*[]);
static int parse_default_value(CdlInterpreter, int, const char*[]);
static void default_value_update_handler(CdlTransaction, CdlNode, CdlProperty, CdlNode, CdlUpdate);
static int parse_dialog(CdlInterpreter, int, const char*[]);
static void dialog_update_handler(CdlTransaction, CdlNode, CdlProperty, CdlNode, CdlUpdate);
static int parse_entry_proc(CdlInterpreter, int, const char*[]);
static int parse_flavor(CdlInterpreter, int, const char*[]);
static int parse_group(CdlInterpreter, int, const char*[]);
static int parse_implements(CdlInterpreter, int, const char*[]);
static void implements_update_handler(CdlTransaction, CdlNode, CdlProperty, CdlNode, CdlUpdate);
static int parse_legal_values(CdlInterpreter, int, const char*[]);
static void legal_values_update_handler(CdlTransaction, CdlNode, CdlProperty, CdlNode, CdlUpdate);
static int parse_requires(CdlInterpreter, int, const char*[]);
static void requires_update_handler(CdlTransaction, CdlNode, CdlProperty, CdlNode, CdlUpdate);
static int parse_wizard(CdlInterpreter, int, const char*[]);
static void wizard_update_handler(CdlTransaction, CdlNode, CdlProperty, CdlNode, CdlUpdate);
// Persistence suppot
void save(CdlInterpreter, Tcl_Channel, int, bool /* modifiable */, bool /* minimal */);
bool value_savefile_entry_needed() const;
static void initialize_savefile_support(CdlToplevel, std::string);
static int savefile_value_source_command(CdlInterpreter, int, const char*[]);
static int savefile_user_value_command(CdlInterpreter, int, const char*[]);
static int savefile_wizard_value_command(CdlInterpreter, int, const char*[]);
static int savefile_inferred_value_command(CdlInterpreter, int, const char*[]);
static int savefile_xxx_value_command(CdlInterpreter, int, const char*[], CdlValueSource);
// Make sure that the current value is legal. This gets called automatically
// by all the members that modify values. It has to be a virtual function
// since some derived classes, e.g. hardware-related valuables, may impose
// constraints over and above legal_values etc.
virtual void check_value(CdlTransaction);
// Similarly check the requires properties
void check_requires(CdlTransaction, CdlProperty_GoalExpression);
void check_requires(CdlTransaction);
// Enabling or disabling a valuable may affect the active state of children
void check_children_active(CdlTransaction);
virtual std::string get_class_name() const;
bool check_this(cyg_assert_class_zeal = cyg_quick) const;
CYGDBG_DECLARE_MEMLEAK_COUNTER();
protected:
CdlValuableBody(CdlValueFlavor = CdlValueFlavor_Bool);
private:
enum {
CdlValuableBody_Invalid = 0,
CdlValuableBody_Magic = 0x2b2acc03
} cdlvaluablebody_cookie;
// Illegal operations
CdlValuableBody(const CdlValuableBody&);
CdlValuableBody& operator=(const CdlValuableBody&);
};
//}}}
//{{{ CdlTransaction etc.
//{{{ Description
// ----------------------------------------------------------------------------
// Transactions. These are used for all changes to a configuration. In some
// cases a transaction is implicit:
//
// valuable->set_value(...)
//
// The actual implementation of this is:
//
// valuable->set_value(...)
// transact = CdlTransactionBody::make(valuable->get_toplevel())
// valuable->set_value(transact, ...)
// <complicated bits>
// transact->commit()
// delete transact
//
// Alternatively the use of transactions may be explicit. For implicit
// uses the library will invoke an inference callback at the
// appropriate time. For explicit transactions this is not necessary.
//
// The commit() operation invokes a transaction callback which should
// not be confused with the inference callback. The former is intended
// for display updates, it specifies everything that has changed
// during the transaction. The latter is used for reporting new
// conflicts to the user, suggesting fixes, etc.
//
// A whole bunch of information is associated with a transaction,
// including: all value changes, details of new conflicts, and details
// of existing conflicts that have gone away. The commit operation
// takes care of updating the toplevel. Until the commit happens
// the toplevel itself remains unchanged. It is also possible to cancel
// a transaction.
//
// An important concept related to transactions is propagation.
// Changing a value may have various effects, for example it may
// change the result of a legal_values list expression, resulting in a
// conflict object having to be created or destroyed. Changing one
// value may result in other value changes, e.g. because of a
// default_value property. All this is "propagation", and may
// happen multiple times within a single transaction.
//
// Transaction objects are also used during load or unload operations,
// but those are a little bit special. In particular it is not possible
// to cancel such a transaction, there will have been updates to the
// toplevel. Using a transaction is convenient because there is a
// need for propagation.
//
// Currently a transaction should be deleted immediately after a
// commit or cancel. This may change in future, in that transaction
// objects can be used to hold undo information.
//
//
// The other big concept related to transactions is inference.
// Changing a value may result in one or more new conflicts being
// created. In some cases the library can figure out for itself how to
// resolve these conflicts, using an inference engine. There are
// parameters to control the operation of the inference engine,
// including whether it runs at all, what changes it is allowed
// to make automatically (usually default and inferred values can
// be updated, but not wizard or user values), and how much
// recursion will happen.
//
// Assuming a default setup in a GUI environment, a typical
// sequence of events would be:
//
// valuable->set_value(...)
// transact = CdlTransactionBody::make(valuable->get_toplevel())
// valuable->set_value(transact, ...)
// transact->set_whole_value(valuable, ...)
// transact->propagate()
// while (!finished)
// transact->resolve()
// <inference>
// invoke inference callback
// transact->apply_solution() (1 or more times)
// transact->set_whole_value(valuable, ...) (1 or more times)
// transact->propagate()
// transact->commit() | transact->cancel()
// delete transact
//
// Note that the propagation steps have to be invoked explicitly,
// allowing multiple changes to be processed in one go. There is
// a utility function which combines the functionality from
// the first propagate() call up to but not including the
// transaction delete operator.
//
//
// The inference engine itself is a complicated beast. There are
// a number of interfaces, but at the end of the day it ends up
// creating a sub-transaction and trying to resolve a single
// conflict in that sub-transaction. The conflict may belong to
// the current transaction or it may be global.
//
// <inference>
// for each conflict of interest
// make sure that there is not already a valid solution
// check that the inference engine can handle it
// create a sub-transaction, associated with the conflict
// apply the conflict resolution code
// if the solution is ok
// install it
// else if the solution might e.g. overwrite a user value
// keep it, the user can decide during the inference callback
//
// The conflict resolution typically works by attempting to change
// one or more values in the sub-transaction, propagating them,
// and seeing what new conflicts get created. If no new conflicts
// get created and one or more existing conflicts go away, groovy.
// Otherwise recursion can be used to try to resolve the new
// conflicts, or other strategies can be explored.
//
// NOTE: what is really necessary is some way of keeping track of the
// "best" solution to date, and allow exploration of alternatives.
// Or possibly keep track of all solutions. That has to be left to
// a future version.
//}}}
//{{{ CdlTransactionCommitCancelOp
// ----------------------------------------------------------------------------
// The CdlTransaction class has built-in knowledge of how to handle values,
// active state, and a few things like that. However there are also more
// complicated operations such as loading and unloading, instantiating
// items, etc. which also need to happen in the context of a transaction
// but which the transaction class does not necessarily know about
// itself - or at least, not in any detail. Since the libcdl core is
// intended to be useful in various contexts, some sort of extensibility
// is essential.
//
// This is achieved by an auxiliary class, CdlTransactionCommitCancelOp.
// Clients of the transaction class can have their own utility class
// which derives from this, and create suitable objects. The transaction
// class maintains a vector of the pending commit/cancel operations.
//
// Each CdlTransactionCommitCancelOp object has two member functions,
// one for when the transaction gets committed and one for when it
// gets cancelled. If a sub-transaction gets committed then its
// pending ops are transferred across to the parent, allowing the
// parent to be cancelled sensibly: the commit ops only get run for
// the toplevel transaction. If a sub-transaction gets cancelled then
// the pending ops are invoked immediately.
//
// There is an assumption that commit/cancel ops get executed strictly
// in FIFO order. Specifically, commit ops get run from first one to
// the last one, allowing later operations in the transaction to
// overwrite earlier ones. Cancel ops get run in reverse order.
class CdlTransactionCommitCancelOp {
friend class CdlTest;
public:
CdlTransactionCommitCancelOp() { }
virtual ~CdlTransactionCommitCancelOp() { };
// The default implementations of both of these do nothing.
// Derived classes should override at least one of these
// functions.
virtual void commit(CdlTransaction transaction) {
CYG_UNUSED_PARAM(CdlTransaction, transaction);
}
virtual void cancel(CdlTransaction transaction) {
CYG_UNUSED_PARAM(CdlTransaction, transaction);
}
protected:
private:
};
//}}}
//{{{ CdlTransaction class
class CdlTransactionBody {
friend class CdlTest;
friend class CdlConflictBody;
friend class CdlValuableBody;
public:
// Create a toplevel transaction
static CdlTransaction make(CdlToplevel);
virtual ~CdlTransactionBody();
CdlToplevel get_toplevel() const;
// Or a sub-transaction. Usually these are created in the context of
// a conflict that is being resolved.
CdlTransaction make(CdlConflict = 0);
CdlTransaction get_parent() const;
CdlConflict get_conflict() const;
// Commit all the changes. Essentially this means transferring
// all of the per-transaction data to the toplevel, and then
// invoking the transaction callback. All propagation, inference,
// etc. should happen before the commit()
// This routine can also be used to transfer changes from a
// sub-transaction to the parent.
void commit();
// A variant of the commit() operation can be used to
// store a sub-transaction in a conflict's solution vector,
// rather than updating the parent transaction. This is useful
// for inferred solutions which cannot be applied without
// user confirmation
void save_solution();
// Can a solution held in a sub-transaction be applied without
// e.g. overwriting a user value with an inferred value?
bool user_confirmation_required() const;
// If the user has explicitly changed a value in the current transaction
// then the inference engine should not undo this or suggest a solution
// that will undo the change.
bool changed_by_user(CdlValuable) const;
// A variant which is used for checking the hierarchy when disabling
// a container
bool subnode_changed_by_user(CdlContainer) const;
// Is one transaction preferable to another?
bool is_preferable_to(CdlTransaction) const;
// Find out about per-transaction conflicts. This is particularly
// useful for the inference callback. The other containers can
// be accessed as well, for completeness.
const std::list<CdlConflict>& get_new_conflicts() const;
const std::list<CdlConflict>& get_new_structural_conflicts() const;
const std::vector<CdlConflict>& get_deleted_conflicts() const;
const std::vector<CdlConflict>& get_deleted_structural_conflicts() const;
const std::vector<CdlConflict>& get_resolved_conflicts() const ;
const std::list<CdlConflict>& get_global_conflicts_with_solutions() const;
const std::map<CdlValuable, CdlValue>& get_changes() const;
const std::set<CdlNode>& get_activated() const;
const std::set<CdlNode>& get_deactivated() const;
const std::set<CdlValuable>& get_legal_values_changes() const;
// Manipulate the current set of conflicts, allowing for nested
// transactions and toplevel conflicts as well.
void clear_conflict(CdlConflict);
bool has_conflict_been_cleared(CdlConflict);
bool has_conflict(CdlNode, bool (*)(CdlConflict));
CdlConflict get_conflict(CdlNode, bool (*)(CdlConflict));
void get_conflicts(CdlNode, bool (*)(CdlConflict), std::vector<CdlConflict>&);
void clear_conflicts(CdlNode, bool (*)(CdlConflict));
bool has_conflict(CdlNode, CdlProperty, bool (*)(CdlConflict));
CdlConflict get_conflict(CdlNode, CdlProperty, bool (*)(CdlConflict));
void get_conflicts(CdlNode, CdlProperty, bool (*)(CdlConflict), std::vector<CdlConflict>&);
void clear_conflicts(CdlNode, CdlProperty, bool (*)(CdlConflict));
bool has_structural_conflict(CdlNode, bool (*)(CdlConflict));
CdlConflict get_structural_conflict(CdlNode, bool (*)(CdlConflict));
void get_structural_conflicts(CdlNode, bool (*)(CdlConflict), std::vector<CdlConflict>&);
void clear_structural_conflicts(CdlNode, bool (*)(CdlConflict));
bool has_structural_conflict(CdlNode, CdlProperty, bool (*)(CdlConflict));
CdlConflict get_structural_conflict(CdlNode, CdlProperty, bool (*)(CdlConflict));
void get_structural_conflicts(CdlNode, CdlProperty, bool (*)(CdlConflict), std::vector<CdlConflict>&);
void clear_structural_conflicts(CdlNode, CdlProperty, bool (*)(CdlConflict));
// During the inference callback the user may decide to
// apply one or more of the solutions.
void apply_solution(CdlConflict);
void apply_solutions(const std::vector<CdlConflict>&);
void apply_all_solutions();
// Cancel all the changes done in this transaction. Essentially
// this just involves clearing out all the STL containers.
void cancel();
// Support for commit/cancel ops. These are used for
// e.g. load and unload operations.
void add_commit_cancel_op(CdlTransactionCommitCancelOp *);
void cancel_last_commit_cancel_op();
CdlTransactionCommitCancelOp* get_last_commit_cancel_op() const;
const std::vector<CdlTransactionCommitCancelOp*>& get_commit_cancel_ops() const;
// Propagation support
void add_active_change(CdlNode);
void add_legal_values_change(CdlValuable);
void propagate();
bool is_propagation_required() const;
// Inference engine support.
void resolve(int = 0); // Process the new conflicts raised by this transaction
void resolve(CdlConflict, int = 0);
void resolve(const std::vector<CdlConflict>&, int = 0);
// An auxiliary function called by the inference engine to perform recursion
bool resolve_recursion(int);
// This function combines propagation, inference, and commit
// in one easy-to-use package
void body();
// Changes.
// There is a call to get hold of a CdlValue reference. Modifications
// should happen via a sequence of the form:
//
// valuable->set_value(transact, ...)
// const CdlValue& old_value = transact->get_whole_value(CdlValuable);
// CdlValue new_value = old_value;
// <modify new_value>
// transact->set_whole_value(CdlValuable, old_value, new_value);
//
// When appropriate the get_whole_value() call takes care of
// updating the current conflict's solution_references vector. The
// set_whole_value() call updated the per-transaction changes map,
// and also stores sufficient information to support propagation.
// set_whole_value() requires both the old and new values, so
// that propagation can be optimized.
const CdlValue& get_whole_value(CdlConstValuable) const;
void set_whole_value(CdlValuable, const CdlValue&, const CdlValue&);
// Control over active vs. inactive also needs to happen inside
// transactions
bool is_active(CdlNode) const;
void set_active(CdlNode, bool);
// Callback and parameter settings
static void (*get_callback_fn())(const CdlTransactionCallback&);
static void set_callback_fn(void (*)(const CdlTransactionCallback&));
static void set_inference_callback_fn(CdlInferenceCallback);
static CdlInferenceCallback get_inference_callback_fn();
static void enable_automatic_inference();
static void disable_automatic_inference();
static bool is_automatic_inference_enabled();
static void set_inference_recursion_limit(int);
static int get_inference_recursion_limit();
// The override indicates the highest level of value source that the
// library can overwrite without needing user confirmation. The
// default value is CdlValueSource_Inferred, indicating that the
// library can overwrite default and inferred values but not
// wizard or user values.
static void set_inference_override(CdlValueSource);
static CdlValueSource get_inference_override();
bool check_this(cyg_assert_class_zeal = cyg_quick) const;
CYGDBG_DECLARE_MEMLEAK_COUNTER();
protected:
private:
CdlTransactionBody(CdlToplevel, CdlTransaction, CdlConflict);
// The associated toplevel and optionally the parent transaction
// and the conflict being worked on
CdlToplevel toplevel;
CdlTransaction parent;
CdlConflict conflict;
// Per-transaction information. All value changes, new conflicts
// etc. first live in the context of a transaction. The global
// configuration only gets updated if the transaction is commited.
// There is also a vector of the pending commit/cancel ops.
std::vector<CdlTransactionCommitCancelOp*> commit_cancel_ops;
std::map<CdlValuable, CdlValue> changes;
std::list<CdlConflict> new_conflicts;
std::list<CdlConflict> new_structural_conflicts;
std::vector<CdlConflict> deleted_conflicts; // Existing global ones
std::vector<CdlConflict> deleted_structural_conflicts;
std::vector<CdlConflict> resolved_conflicts; // New ones already fixed by the inference engine
std::list<CdlConflict> global_conflicts_with_solutions;
std::set<CdlNode> activated;
std::set<CdlNode> deactivated;
std::set<CdlValuable> legal_values_changes;
bool dirty;
// Change propagation. It is necessary to keep track of all
// pending value changes, active changes, and of things being
// loaded or unloaded. The set_value() call is used to update the
// value_changes container.
std::deque<CdlValuable> value_changes;
std::deque<CdlNode> active_changes;
// Control over the inference engine etc.
static CdlInferenceCallback inference_callback;
static bool inference_enabled;
static int inference_recursion_limit;
static CdlValueSource inference_override;
static void (*callback_fn)(const CdlTransactionCallback&);
enum {
CdlTransactionBody_Invalid = 0,
CdlTransactionBody_Magic = 0x3f91e4df
} cdltransactionbody_cookie;
// Illegal operations
CdlTransactionBody();
CdlTransactionBody(const CdlTransactionBody &);
CdlTransactionBody& operator=(const CdlTransactionBody&);
};
//}}}
//{{{ CdlTransactionCallback
// ----------------------------------------------------------------------------
// The callback class is used to inform applications about all the
// changes that are happening, including side effects. Application
// code can install a callback function which gets invoked at the
// end of every transaction.
//
// NOTE: this implementation is preliminary. In particular it is
// not extensible, it only deals with changes relevant to software
// configurations.
class CdlTransactionCallback {
friend class CdlTest;
friend class CdlTransactionBody;
public:
~CdlTransactionCallback();
static void (*get_callback_fn())(const CdlTransactionCallback&);
static void set_callback_fn(void (*)(const CdlTransactionCallback&));
// Callback functions should be able to retrieve information
// about the current transaction and toplevel, to avoid the use
// of statics.
CdlTransaction get_transaction() const;
CdlToplevel get_toplevel() const;
// active_changes and legal_values_changes get updated as the
// transaction proceeds, so a set implementation is more
// efficient. The others get filled in during a commit operation.
// A transaction may result in multiple conflicts for a given node
// being eliminated, so again a set is appropriate. For the others
// there is no possibility of duplicates so a vector is better.
std::vector<CdlValuable> value_changes;
std::vector<CdlNode> active_changes;
std::vector<CdlValuable> legal_values_changes;
std::vector<CdlValuable> value_source_changes;
std::vector<CdlConflict> new_conflicts;
std::vector<CdlConflict> new_structural_conflicts;
std::vector<CdlNode> nodes_with_resolved_conflicts;
std::vector<CdlNode> nodes_with_resolved_structural_conflicts;
bool check_this(cyg_assert_class_zeal = cyg_quick) const;
protected:
private:
CdlTransactionCallback(CdlTransaction);
CdlTransaction transact;
// Illegal operation.
CdlTransactionCallback();
enum {
CdlTransactionCallback_Invalid = 0,
CdlTransactionCallback_Magic = 0x0cec3a95
} cdltransactioncallback_cookie;
};
//}}}
//{{{ CdlLocalTransaction
// ----------------------------------------------------------------------------
// A utility class to create a per-function transaction object which gets
// cleaned up automatically should an exception happen.
class CdlLocalTransaction {
friend class CdlTrest;
public:
CdlLocalTransaction(CdlToplevel toplevel) {
transaction = CdlTransactionBody::make(toplevel);
}
~CdlLocalTransaction() {
// The destructor may get invoked during exception handling.
// It is assumed that cancelling the transaction would be a
// good thing when that happens. Normal operation should
// go through the body() or commit() members, which clear
// the transaction field.
// There is a slight consistency here. Normally after a
// transaction commit the transaction object is still
// around. Here the transaction object get deleted. This
// is unlikely to matter in practice.
if (0 != transaction) {
transaction->cancel();
delete transaction;
}
}
CdlTransaction get() {
return transaction;
}
void body() {
transaction->body();
delete transaction;
transaction = 0;
}
void commit() {
transaction->commit();
delete transaction;
transaction = 0;
}
void propagate() {
transaction->propagate();
}
void destroy() {
if (0 != transaction) {
transaction->cancel();
delete transaction;
transaction = 0;
}
}
private:
CdlTransaction transaction;
CdlLocalTransaction();
};
//}}}
//}}}
//{{{ Build and define information
//{{{ Description
// ----------------------------------------------------------------------------
// There are two related concepts: buildable components, and
// definable components. The former typically refers to compiling
// sources files to produce libraries, although other types of build
// are possible. The latter refers to generating header files
// containing the current configuration data. Typically any loadable
// that is buildable is also definable, so that the source files can
// #include the appropriate generated headers and adapt to the
// configuration data that way. The inverse is not true: for example
// in HCDL it may be appropriate to generate a header file but there
// is nothing to be compiled, device drivers are software packages.
//
// The relevant base classes are as follows:
//
// 1) CdlBuildable - this object can have build-related properties.
// All buildables are also valuables.
// 2) CdlBuildLoadable - this is a base class for loadables, providing
// some extra properties that are relevant for
// loadables that can involve builds.
// 3) CdlDefinable - this object can result in #define's in a
// header file. All exportables are also
// valuables.
// 4) CdlDefineLoadable - this is a base class for any loadables that
// can contain buildables.
//
// Support for both buildable and exportable components is part of the
// core library for now. This may change in future, depending on how
// many CDL variants get implemented.
//
// There are various properties related to building. First, the
// ones applicable to the CdlBuildLoadable class.
//
// 1) library xyz.
// This specifies the default library for anything built in this
// loadable. If there is no library property then it defaults to
// libtarget.a (or rather to a class static that happens to be
// initialized to libtarget.a)
//
// 2) include_dir <dir>.
// This specifies where the loadable's exported header files should
// end up. The default value is the toplevel, but e.g. the eCos
// kernel specifies an include_dir of cyg/kernel. Note that fixed
// header files are associated with buildables, not definables,
// the latter deal with generated header files only.
//
// 3) include_files <hdr1 hdr2 ...>
// The recommended directory hierarchy for non-trivial packages
// involves separate subdirectories src, include, cdl, doc, and
// test. This is too heavyweight for very simple packages where it
// is better to keep everything in just one directory. However that
// introduces a potential conflict between public and private
// header files, which can be resolved by the include_files
// property. The actual rules are:
//
// a) if there an include_files property, that lists all the
// headers that should be exported.
//
// b) else if there is an include subdirectory, it is assumed that
// all files below that should be exported.
//
// c) otherwise all files matching a suitable glob pattern should
// be exported. The default pattern is *.h *.hxx *.inl, but can
// be overwritten.
//
// 4) makefile <file>
// This allows component developers to provide a GNU makefile to be
// used for building, rather than specify the relevant information
// via properties.
// NOTE: this property is ignored for now. It is roughly
// equivalent to a custom build step where the command is
// "make -C <dir> -f <file>", but in addition it is necessary to
// worry about phony targets for default, clean, etc.
//
// A DefineLoadable adds the following property:
//
// 1) define_header <file>
// This specifies the header file that will be generated. If this
// property is absent then the library will generate a default one
// based on the loadable's name, by discarding everything up to and
// including the first underscore, lowercasing the rest, and
// appending .h. For example, CYGPKG_KERNEL would result in a
// header file kernel.h.
//
// Hardware packages have an implicit "define_header hardware.h"
// property.
//
// A buildable has the following properties:
//
// 1) compile [-library xyz] <file1> <file2> ...
// This specifies one or more files that need to be compiled.
// By default the resulting object files will go into the
// current library (set via a higher-level library or
// defaulting to libtarget.a).
//
// Legitimate filename suffixes for compile statements are .c, .cxx
// and .S. Further suffixes may be supported in future. In the
// long term we will need some external data files defining how
// the various suffixes should be handled.
//
// Associated with every compilation are details of the compiler to
// be used and the compiler flags. For now no attempt is made
// to do anything interesting in this area, although there is
// sufficient information in the database for the needs of
// command line tools.
//
// Longer term there are complications. Packages may want some
// control over the compiler flags that should be used, e.g.
// "requires {!(flags ~= ".*-fno-rtti.*")}" to guarantee that the
// compiler flags do not include -fno-rtti, rather useful if the
// package's source code depends on that language feature. Mixed
// architecture systems (e.g. ARM/Thumb) will cause problems when
// it comes to selecting the compiler. The exact means by which
// all this will work is not yet clear.
//
// 2) object [-library xyz] <file1> <file2> ...
// This specifies one or more pre-built object files that should
// go into the appropriate library.
//
// The problem here is coping with different architectures, and for
// many architectures it will also be necessary to worry about
// multilibs. Third party component vendors are unlikely to supply
// separate object files for every supported architecture and every
// valid multilib within those architectures, so there are
// constraints on the multilib-related compiler flags used for
// building other packages and the application itself.
//
// NOTE: this property is ignored for now.
//
// 3) make_object [-library xyz] [-priority pri] <file> <makefile fragment>
//
// For example:
//
// make_object toyslock.o {
// toyslock.o : toyslock.y
// yacc toyslock.y
// $(CC) $(CFLAGS) -o toyslock.o y.tab.c
// }
//
// This defines a custom build step for an object file that
// should go into a particular directory. A makefile syntax
// is used to define the rule simply because it is likely
// to be familiar to package developers, and does not
// imply that the builds will happen via a makefile.
//
// The optional priority field indicates at which stage during
// the build the rule should trigger. The default value is
// 100, which is the same as for all files specified in
// "compile" properties. A lower value means that the object
// will be generated earlier. Libraries are generated at
// priority 200, and "make" properties normally execute at
// priority 300.
// NOTE: it is not clear yet whether supporting priorities
// in this way is a good idea, or whether the dependencies
// information could be used instead.
//
// Unresolved issues:
//
// a) what commands can be used in the build rules? There
// should be a core set of supported commands, as per
// an eCos toolchain build. It should also be possible
// for packages to provide their own host tools.
//
// For sourceware folks, moving away from a single toolchain
// tarball and expecting them to download and install
// egcs, binutils and gdb separately is actually a bad
// idea in this regard, it makes it much more likely that
// some users will have an incomplete tools installation and
// hence that builds will fail.
//
// b) there is an obvious need for variable substitution in the
// rules, e.g. $(CC). At what stage do these variables get
// expanded, and where does the required information live?
//
// c) who is responsible for header file dependency analysis?
// Should the rules be modified automatically to do this,
// or do we leave this to the package developer? It may be
// very hard to do the former, but the latter will cause
// problems for IDE integration.
//
// d) in which directory will the rules get run? What prevents
// filename conflicts between different packages?
//
// NOTE: make_object is not actually required just yet, but the
// issues are much the same as for the "make" property which is
// required.
//
// 4) make [-priority pri] <target> <makefile fragment>
//
// For example:
//
// make target.ld {
// target.ld : arm.ld
// $(CC) -E -P -xc $(CFLAGS) -o $@ $<
// }
//
// This defines a custom build step for a target that is not going
// to end up in a library. The main such targets at the moment are
// the linker script, vectors.o, and extras.o, but there may well
// be others in future.
//
// The default priority for "make" properties is 300, which means
// that the build rules trigger after all normal compilations and
// after the libraries are generated. It is possible to specify
// custom build steps that should run before any compilations
// using a priority < 100.
//
// Unresolved issues:
//
// a) what commands can be used?
//
// b) variable substitution?
//
// c) header file dependency analysis?
//
// d) directories and filenames?
//
// e) where should the resulting files end up? Currently they can
// all go into $(PREFIX)/lib, but in the long term we may
// need to be a bit more flexible.
//
// 5) build_proc <tcl code>
//
// This defines some Tcl code that should be run prior to any
// build, for example to generate a source file. It must run
// within the appropriate loadable's Tcl interpreter so that
// it can query the current configuration.
//
// NOTE: this property is not implemented yet.
//
//
// A definable has the following properties:
//
// 1) no_define
// Usually the library will generate either one or two #define's
// for every definable, inside the current header file. This can be
// suppressed by the no_define property, which is typically
// accompanied by some other #define-related property such as
// define_proc or define.
//
// 2) define [-file <filename>] [-format <format_string>] symbol
// This will result in an additional #define for the specified
// symbol in the specified file. The only filenames that are valid
// are the loadable's current filename (as per define_header), and
// the global header file system.h. Use of the latter should be
// avoided.
//
// The optional format string behaves as per the define_format
// property below.
//
// 3) define_format <format_string>
// This is only relevant for booldata or data flavors. By default
// two #define's will be generated (assuming the valuable is active
// and enabled):
//
// #define <symbol> value
// #define <symbol>_value
//
// The latter will only be generated if the resulting symbol is
// a valid C preprocessor symbol, and is intended to allow the
// use of #ifdef as well as #ifdef (useful if the value is
// non-numerical).
//
// The define_format property provides control over the first of
// these two #defines. The net result is that the #define will be
// generated by evaluating the following Tcl fragment:
//
// set result "#define <symbol> [<format> <value>]"
//
// Command and variable substitution are available if desired,
// but for anything that complicated the define_proc property
// is normally more useful.
//
// define_format is only applicable to the default definition,
// so it cannot be used in conjunction with no_define. The
// define property supports a -format option.
//
// 4) define_proc <tclcode>
// This specifies some Tcl code that should be run when header
// file generation takes place, in addition to any #define's
// generated by default or courtesy of define properties.
// The define_proc property is commonly used in conjunction with
// no_define, but this is not required.
//
// There will be two channels already set up: cdl_header
// for the current loadable, and cdl_system_header for system.h.
// Writing data to system.h should be avoided.
//
// 5) if_define <condition> <symbol>
// This property provides direct support for a common programming
// paradigm. It allows direct generation of code like the
// following:
//
// #ifdef CYGSRC_TOYS_BLOCKS
// # define CYGDBG_INFRA_USE_PRECONDITIONS 1
// #endif
//
// In this case CYGSRC_TOYS_BLOCKS is the condition and
// CYGDBG_INFRA_USE_PRECONDITIONS is the symbol. The
// #ifdef/#define sequence will be generated in addition to
// any other #define's resulting from the default behaviour,
// the define property, or the define_proc property. It is
// not affected by no_define.
//}}}
//{{{ The build process
// ----------------------------------------------------------------------------
// For command-line operation the steps involved in doing a build are:
//
// 1) work out what needs to be built.
//
// 2) generate a build and install tree. This involves making sure that
// the various directories exist and are accessible.
//
// 3) generate or update the toplevel makefile.
//
// 4) generate the configuration header files.
//
// For operation in an IDE steps (2) and (3) will be handled by
// different code.
//
// There is a library call to get hold of all the build information:
//
// config->get_build_info(CdlBuildInfo &info);
//
// This erases anything previously present in the build-info argument
// and fills in the information appropriate to the current
// configuration, essentially by walking down the list of loadables
// and each loadable's list of nodes, checking for BuildLoadables
// and Buildables along the way. The BuildInfo class is defined
// further down.
//
// An alternative library call can be used to find out about all
// possible files that need to be compiled etc., irrespective of the
// current configuration settings. This could be useful when it
// comes to letting the user control compiler flags etc.
//
// config->get_all_build_info(CdlBuildInfo& info);
//
// There is another library call for step (4):
//
// config->generate_config_headers(std::string directory)
//
// This will create or update the header files appropriate to
// the current configuration. Temporary files will be generated,
// diff'ed with the current version, and existing files will
// only be modified if necessary. The directory argument
// indicates where the header files should go, i.e. it should
// be the equivalent of $(PREFIX)/include/pkgconf
//
// This library call does not delete any files it does not
// recognize, that is the responsibility of higher-level code.
// It is possible to get or update a list of the files that
// will be generated:
//
// config->get_config_headers(std::vector<std::string>& headers)
//
// The argument will be cleared if necessary and then filled in with
// the current set of header files. Higher level code can compare the
// result with the current files in the directory and take or suggest
// remedial action.
//
// There is also a library call which combines all four stages:
//
// config->generate_build_tree(std::string build_tree, std::string prefix = $(BUILD)/install)
//
//
// The order in which the various build steps happen is important.
//
// 1) non-configuration headers must be copied from the component
// repository into $(PREFIX)/include. No compiles can happen
// before this.
//
// 2) all compile properties can happen in parallel. These have an
// effective priority of 100.
//
// 3) all make_object priorities can happen in parallel with
// compiles. These have a default priority of 100, but the
// priority can be modified.
//
// 4) the generated objects and any pre-built objects should be
// incorporated into the appropriate library. This happens
// at priority 200.
//
// 5) custom build steps associated with "make" properties should
// now run. These have a default priority of 300, but it is
// possible to override this.
//
// Usually all source files will come from the component repository,
// which means that they are read-only. Ideally it should also be
// possible for a source file to be copied into the build tree and
// edited there, and subsequent builds should pick up the copy rather
// than the original. The build data generated by libcdl will always
// be in the form of relative pathnames to facilitate this.
//}}}
//{{{ CdlBuildInfo class
// ----------------------------------------------------------------------------
// Extracting the build information.
//
// libcdl.a defines the following classes related to build information.
//
// CdlBuildInfo
// CdlBuildInfo_Loadable
// CdlBuildInfo_Header
// CdlBuildInfo_Compile
// CdlBuildInfo_Object
// CdlBuildInfo_MakeObject
// CdlBuildInfo_Make
//
// The build information is organized on a per-loadable basis.
// Higher-level code may choose to flatten this or to keep the
// distinction. A CdlBuildInfo object is primarily a vector of
// CdlBuildInfo_Loadable objects. CdlBuildInfo objects can be created
// statically.
//
// In turn, each CdlBuildInfo_Loadable object is primarily a
// collection of five vectors, one each for Header, Compile, Object,
// MakeObject and Make.
//
// All pathnames in these data structures will use forward slashes as
// the directory separator, irrespective of the host platform. All
// pathnames will be relative.
struct CdlBuildInfo_Header {
std::string source; /* include/cyg_ass.h */
std::string destination; /* cyg/infra/cyg_ass.h */
};
struct CdlBuildInfo_Compile {
std::string library; /* libtarget.a */
std::string source; /* src/fancy.cxx */
// Compiler and cflags data may be added in future.
};
struct CdlBuildInfo_Object {
std::string library; /* libtarget.a */
std::string object; /* obj/hello.o */
};
struct CdlBuildInfo_MakeObject {
cdl_int priority; /* 100 */
std::string library; /* libtarget.a */
std::string object; /* toyslock.o */
std::string deps; /* toyslock.y */
/*
It is not clear whether the deps field is actually useful in the
context of IDE integration, but see the note about arm.inc
above.
*/
std::string rules;
/*
A typical value for "rules" might be:
yacc toyslock.y
$(CC) $(CFLAGS) -o toyslock.o y.tab.c
Leading white space is not significant. Newlines are significant.
Backslash escapes in the text will not have been processed yet.
*/
};
struct CdlBuildInfo_Make {
cdl_int priority; /* 300 */
std::string target; /* extras.o */
std::string deps; /* libextras.a */
std::string rules;
/*
Something like:
$(CC) $(ARCHFLAGS) $(LDARCHFLAGS) -nostdlib -Wl,-r -Wl,--whole-archive $(PREFIX)/lib/libextras.a -o $(PREFIX)/lib/extras.o
*/
};
class CdlBuildInfo_Loadable {
friend class CdlTest;
public:
std::string name; /* CYGPKG_INFRA */
std::string directory; /* infra/current */
std::vector<CdlBuildInfo_Header> headers;
std::vector<CdlBuildInfo_Compile> compiles;
std::vector<CdlBuildInfo_Object> objects;
std::vector<CdlBuildInfo_MakeObject> make_objects;
std::vector<CdlBuildInfo_Make> makes;
protected:
private:
};
class CdlBuildInfo {
friend class CdlTest;
public:
std::vector<CdlBuildInfo_Loadable> entries;
protected:
private:
};
//}}}
//{{{ CdlBuildLoadable
// ----------------------------------------------------------------------------
// BuildLoadables are derived from Loadables and are appropriate for
// any loadables that can contain build information. There are a
// number of properties applicable at this level: makefile,
// include_dir, include_files and library. The main interface of
// interest is update_build_info().
//
// It is likely that all BuildLoadables are also Buildables, but this
// is not required.
class CdlBuildLoadableBody : virtual public CdlLoadableBody
{
friend class CdlTest;
public:
virtual ~CdlBuildLoadableBody();
// This is the main way to extract information about what should
// get built. It takes into account the active and enabled states,
// as appropriate.
void update_build_info(CdlBuildInfo&) const;
// An alternative which ignores the active and enabled states.
void update_all_build_info(CdlBuildInfo&) const;
// Property parsers and validation code appropriate for a
// build-loadable object such as makefile
static void add_property_parsers(std::vector<CdlInterpreterCommandEntry>& parsers);
void check_properties(CdlInterpreter);
static int parse_library(CdlInterpreter, int, const char*[]);
static int parse_makefile(CdlInterpreter, int, const char*[]);
static int parse_include_dir(CdlInterpreter, int, const char*[]);
static int parse_include_files(CdlInterpreter, int, const char*[]);
// By default any compiled files will go into libtarget.a, which
// is the default value for this variable. Individual applications may
// specify an alternative default library.
static char* default_library_name;
// When filling in a build_info structure the library needs to know
// what constitutes a header file. A glob pattern can be used for this.
// NOTE: in the long term this should come out of a data file.
static char* default_headers_glob_pattern;
virtual std::string get_class_name() const;
bool check_this(cyg_assert_class_zeal = cyg_quick) const;
CYGDBG_DECLARE_MEMLEAK_COUNTER();
protected:
CdlBuildLoadableBody();
private:
enum {
CdlBuildLoadableBody_Invalid = 0,
CdlBuildLoadableBody_Magic = 0x55776643
} cdlbuildloadablebody_cookie;
// Illegal operations
CdlBuildLoadableBody(const CdlBuildLoadableBody&);
CdlBuildLoadableBody& operator=(const CdlBuildLoadableBody&);
};
//}}}
//{{{ CdlBuildable
// ----------------------------------------------------------------------------
// Buildable objects can have properties such as compile and
// make_object. These properties are not normally accessed
// directly. Instead there is a member function to update a
// CdlBuildInfo_Loadable object.
//
// The build properties for a given buildable have an effect iff
// that buildable is active, and in addition if the buildable is also
// a valuable then it must be enabled.
class CdlBuildableBody : virtual public CdlNodeBody
{
friend class CdlTest;
public:
virtual ~CdlBuildableBody();
// This is the main way to extract information about what should
// get built. It takes into account the active and enabled states,
// as appropriate. The second argument indicates the default
// library for the current loadable.
void update_build_info(CdlBuildInfo_Loadable&, std::string) const;
// An alternative which ignores the active and enabled states.
void update_all_build_info(CdlBuildInfo_Loadable&, std::string) const;
// Add property parsers and validation code appropriate for a
// buildable object such as compile and make_object
static void add_property_parsers(std::vector<CdlInterpreterCommandEntry>& parsers);
void check_properties(CdlInterpreter);
static int parse_build_proc(CdlInterpreter, int, const char*[]);
static int parse_compile(CdlInterpreter, int, const char*[]);
static int parse_make(CdlInterpreter, int, const char*[]);
static int parse_make_object(CdlInterpreter, int, const char*[]);
static int parse_object(CdlInterpreter, int, const char*[]);
static bool split_custom_build_step(std::string /* data */, std::string& /* target */, std::string& /* deps */,
std::string& /* rules*/, std::string& /* error_msg */);
virtual std::string get_class_name() const;
bool check_this(cyg_assert_class_zeal = cyg_quick) const;
CYGDBG_DECLARE_MEMLEAK_COUNTER();
protected:
CdlBuildableBody();
private:
enum {
CdlBuildableBody_Invalid = 0,
CdlBuildableBody_Magic = 0x16eb1c04
} cdlbuildablebody_cookie;
// Illegal operations
CdlBuildableBody(const CdlBuildableBody&);
CdlBuildableBody& operator=(const CdlBuildableBody&);
};
//}}}
//{{{ CdlDefineLoadable
// ----------------------------------------------------------------------------
// DefineLoadables are derived from Loadables and are appropriate for
// any loadables that can result in generated header files containing
// configuration data. There is one applicable property,
// define_header. The main interface of interest is
// generate_config_headers().
class CdlDefineLoadableBody : virtual public CdlLoadableBody
{
friend class CdlTest;
public:
virtual ~CdlDefineLoadableBody();
// Update the header file for this loadable. The first argument
// is a channel to the loadable-specific header file. The second
// argument is a channel to the global header file.
void generate_config_header(Tcl_Channel, Tcl_Channel) const;
// What header file should be generated for this loadable?
virtual std::string get_config_header() const;
// Add property parsers and validation code.
static void add_property_parsers(std::vector<CdlInterpreterCommandEntry>& parsers);
void check_properties(CdlInterpreter);
static int parse_define_header(CdlInterpreter, int, const char*[]);
virtual std::string get_class_name() const;
bool check_this(cyg_assert_class_zeal = cyg_quick) const;
CYGDBG_DECLARE_MEMLEAK_COUNTER();
protected:
CdlDefineLoadableBody();
private:
enum {
CdlDefineLoadableBody_Invalid = 0,
CdlDefineLoadableBody_Magic = 0x7e211709
} cdldefineloadablebody_cookie;
// Illegal operations
CdlDefineLoadableBody(const CdlDefineLoadableBody&);
CdlDefineLoadableBody& operator=(const CdlDefineLoadableBody&);
};
//}}}
//{{{ CdlDefinable
// ----------------------------------------------------------------------------
// Definables are derived from Valuables and provide support for
// outputting a configuration header file.
class CdlDefinableBody : virtual public CdlValuableBody
{
friend class CdlTest;
public:
virtual ~CdlDefinableBody();
// Update the header file for this definable. The loadable's Tcl
// interpreter will already have channels cdl_header and
// cdl_system_header set up appropriately.
void generate_config_header( Tcl_Channel, Tcl_Channel) const;
// Add property parsers and validation code.
static void add_property_parsers(std::vector<CdlInterpreterCommandEntry>& parsers);
void check_properties(CdlInterpreter);
static int parse_define(CdlInterpreter, int, const char*[]);
static int parse_define_format(CdlInterpreter, int, const char*[]);
static int parse_define_proc(CdlInterpreter, int, const char*[]);
static int parse_if_define(CdlInterpreter, int, const char*[]);
static int parse_no_define(CdlInterpreter, int, const char*[]);
virtual std::string get_class_name() const;
bool check_this(cyg_assert_class_zeal = cyg_quick) const;
CYGDBG_DECLARE_MEMLEAK_COUNTER();
protected:
CdlDefinableBody();
private:
enum {
CdlDefinableBody_Invalid = 0,
CdlDefinableBody_Magic = 0x65a2c95a
} cdldefinablebody_cookie;
// Illegal operations
CdlDefinableBody(const CdlDefinableBody&);
CdlDefinableBody& operator=(const CdlDefinableBody&);
};
//}}}
//}}}
//{{{ CdlDialog
// ----------------------------------------------------------------------------
// A dialog simply inherits from CdlUserVisible and provides convenient
// access to several dialog-specific properties.
class CdlDialogBody :
public virtual CdlUserVisibleBody,
public virtual CdlParentableBody
{
friend class CdlTest;
public:
virtual ~CdlDialogBody();
// Dialogs may be enabled or disabled globally. This affects
// CdlValuable::get_widget_hint() if the valuable has an associated
// custom dialog.
static void disable_dialogs();
static void enable_dialogs();
static bool dialogs_are_enabled();
bool has_init_proc() const;
bool has_update_proc() const;
const cdl_tcl_code& get_init_proc() const;
const cdl_tcl_code& get_update_proc() const;
const cdl_tcl_code& get_display_proc() const;
const cdl_tcl_code& get_confirm_proc() const;
const cdl_tcl_code& get_cancel_proc() const;
static int parse_dialog(CdlInterpreter, int, const char*[]);
static int parse_display_proc(CdlInterpreter, int, const char*[]);
static int parse_update_proc(CdlInterpreter, int, const char*[]);
// Persistence support. Dialogs should just be ignored when it
// comes to saving and restoring files.
virtual void save(CdlInterpreter, Tcl_Channel, int, bool);
virtual std::string get_class_name() const;
bool check_this(cyg_assert_class_zeal = cyg_quick) const;
CYGDBG_DECLARE_MEMLEAK_COUNTER();
private:
// The constructor only gets invoked from inside parse_dialog()
CdlDialogBody(std::string);
static bool dialogs_enabled;
enum {
CdlDialogBody_Invalid = 0,
CdlDialogBody_Magic = 0x3f4df391
} cdldialogbody_cookie;
// Illegal operations. The dialog name must be known at the time
// that the object is constructed.
CdlDialogBody();
CdlDialogBody(const CdlDialogBody&);
CdlDialogBody& operator=(const CdlDialogBody&);
};
//}}}
//{{{ CdlWizard
// ----------------------------------------------------------------------------
// A wizard is very much like a dialog, just a different set of properties.
class CdlWizardBody :
public virtual CdlUserVisibleBody,
public virtual CdlParentableBody
{
friend class CdlTest;
public:
virtual ~CdlWizardBody();
bool has_init_proc() const;
bool has_decoration_proc() const;
const cdl_tcl_code& get_init_proc() const;
const cdl_tcl_code& get_decoration_proc() const;
const cdl_tcl_code& get_confirm_proc() const;
const cdl_tcl_code& get_cancel_proc() const;
bool has_screen(cdl_int) const;
cdl_int get_first_screen_number() const;
const cdl_tcl_code& get_first_screen() const;
const cdl_tcl_code& get_screen(cdl_int) const;
static int parse_wizard(CdlInterpreter, int, const char*[]);
static int parse_cancel_proc(CdlInterpreter, int, const char*[]);
static int parse_confirm_proc(CdlInterpreter, int, const char*[]);
static int parse_decoration_proc(CdlInterpreter, int, const char*[]);
static int parse_init_proc(CdlInterpreter, int, const char*[]);
static int parse_screen(CdlInterpreter, int, const char*[]);
// Persistence support. Wizards should just be ignored when it
// comes to saving and restoring files.
virtual void save(CdlInterpreter, Tcl_Channel, int, bool);
virtual std::string get_class_name() const;
bool check_this(cyg_assert_class_zeal = cyg_quick) const;
CYGDBG_DECLARE_MEMLEAK_COUNTER();
private:
// The constructor only gets invoked from inside parse_wizard().
CdlWizardBody(std::string);
// Illegal operations.
CdlWizardBody();
CdlWizardBody(const CdlWizardBody&);
CdlWizardBody& operator=(const CdlWizardBody&);
enum {
CdlWizardBody_Invalid = 0,
CdlWizardBody_Magic = 0x4ec1c39a
} cdlwizardbody_cookie;
};
//}}}
//{{{ CdlInterface class
// ----------------------------------------------------------------------------
// Similarly for interfaces.
class CdlInterfaceBody : public virtual CdlNodeBody,
public virtual CdlUserVisibleBody,
public virtual CdlValuableBody,
public virtual CdlParentableBody,
public virtual CdlBuildableBody,
public virtual CdlDefinableBody
{
friend class CdlTest;
public:
~CdlInterfaceBody();
void get_implementers(std::vector<CdlValuable>&) const;
void recalculate(CdlTransaction);
static int parse_interface(CdlInterpreter, int, const char*[]);
// Persistence support. The interface data cannot sensibly be modified
// by users, it is all calculated. However it is useful to have the
// interface data present in the saved file so that users can examine
// dependencies etc.
virtual void save(CdlInterpreter, Tcl_Channel, int, bool);
static void initialize_savefile_support(CdlToplevel);
static int savefile_interface_command(CdlInterpreter, int, const char*[]);
bool was_generated() const;
virtual bool is_modifiable() const;
virtual std::string get_class_name() const;
bool check_this(cyg_assert_class_zeal = cyg_quick) const;
CYGDBG_DECLARE_MEMLEAK_COUNTER();
private:
CdlInterfaceBody(std::string, bool /* generated */);
bool generated;
enum {
CdlInterfaceBody_Invalid = 0,
CdlInterfaceBody_Magic = 0x67f7fbe5
} cdlinterfacebody_cookie;
CdlInterfaceBody();
CdlInterfaceBody(const CdlInterfaceBody&);
CdlInterfaceBody& operator=(const CdlInterfaceBody&);
};
//}}}
#endif /* !__CDLCORE_HXX */
// EOF cdlcore.hxx
Go to most recent revision | Compare with Previous | Blame | View Log