Mercurial > flash_v2
diff host/libcdl/cdlcore.hxx @ 76:435cced73e2f ecos-v1_3_1-release
eCos v1.3.1 merged from eCos master repository on 2000-03-27-23:22:51-BST
| author | jlarmour |
|---|---|
| date | Tue, 28 Mar 2000 14:10:45 +0000 |
| parents | |
| children | 6736c52df507 |
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/host/libcdl/cdlcore.hxx @@ -0,0 +1,5530 @@ +#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) 1999, 2000 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> + +// 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, 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; + } + }; + + // 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. + +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. The result string is also available. + int eval(std::string, std::string&); + + // Ditto for any Tcl code that comes from CDL files + int eval_cdl_code(const cdl_tcl_code, std::string&); + + // And support for evaluating an entire file + 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. This is useful for e.g. + // Tcl_OpenFileChannel which leaves a result string in the interpreter + std::string get_result(); + + // 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) throw(CdlInputOutputException); + + // 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_filename() 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_filename(std::string); + void pop_filename(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); + + // 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, 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 filename; + CdlDiagnosticFnPtr error_fn_ptr; + CdlDiagnosticFnPtr warning_fn_ptr; + + 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, CdlProperty, CdlToplevel = 0); + ~CdlEvalContext(); + + 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 +}; + +// ---------------------------------------------------------------------------- +// 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. + +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. +}; + +//}}} +//{{{ 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) + throw(CdlParseException,std::bad_alloc); + + // 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&) + throw(CdlParseException, std::bad_alloc); + + // 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&) + throw(CdlParseException, std::bad_alloc); + + // 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&) + throw(CdlEvalException, std::bad_alloc); + void eval_internal(CdlEvalContext&, CdlSimpleValue&) + throw(CdlEvalException, std::bad_alloc); + + // 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) + throw(CdlParseException, std::bad_alloc); + + // Evaluation support. A list expression evaluates to a list value. + void eval(CdlEvalContext&, CdlListValue&) + throw(CdlEvalException, std::bad_alloc); + + // 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&) + throw(CdlEvalException, std::bad_alloc); + bool is_member(CdlEvalContext&, std::string) + throw(CdlEvalException, std::bad_alloc); + bool is_member(CdlEvalContext&, cdl_int) + throw(CdlEvalException, std::bad_alloc); + bool is_member(CdlEvalContext&, double) + throw(CdlEvalException, std::bad_alloc); + + // 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&) throw(CdlEvalException, std::bad_alloc); + + 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) + throw(CdlParseException, std::bad_alloc); + + // 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&) + throw(CdlEvalException, std::bad_alloc); + void eval(CdlEvalContext&, bool&) + throw(CdlEvalException, std::bad_alloc); + + // 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&) throw(CdlEvalException, std::bad_alloc); + + std::string expression_string; + + enum { + CdlGoalExpressionBody_Invalid = 0, + CdlGoalExpressionBody_Magic = 0x5a58bb24 + } cdlgoalexpressionbody_cookie; +}; + +//}}} + +//}}} +//{{{ 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, 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, 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, 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, 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, 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, char**, + std::vector<std::pair<std::string,std::string> >&); + static CdlProperty_TclCode make(CdlNode, std::string, cdl_int, cdl_tcl_code, int, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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 const char* get_tcl_cmd_name(const char*); + static std::string concatenate_argv(int, char**, int); + static int parse_options(CdlInterpreter, std::string /* diag_prefix */, char** /* options */, + int /* argc */, char** /* argv */, int /* start_index */, + std::vector<std::pair<std::string,std::string> >& /* result */); + static std::string get_diagnostic_prefix(CdlInterpreter); + static void report_error(CdlInterpreter, std::string); + static void report_warning(CdlInterpreter, std::string); + static void clear_error_count(CdlInterpreter); + static int get_error_count(CdlInterpreter); + static void incr_error_count(CdlInterpreter, int=1); + + // Support for Tcl's "unknown" command + static int unknown_command(CdlInterpreter, int, char**); + + // Property-related utilities + static std::string get_property_prefix(char*); + static std::string get_property_prefix(CdlProperty); + static void report_property_parse_error(CdlInterpreter, char*, std::string); + static void report_property_parse_error(CdlInterpreter, CdlProperty, std::string); + + // Utility parsing routines + static int parse_minimal_property(CdlInterpreter, int, char**, std::string, + char**, void (*)(CdlInterpreter, CdlProperty_Minimal)); + static int parse_string_property(CdlInterpreter, int, char**, std::string, + char**, void (*)(CdlInterpreter, CdlProperty_String)); + static int parse_tclcode_property(CdlInterpreter, int, char**, std::string, + char**, void (*)(CdlInterpreter, CdlProperty_TclCode)); + static int parse_stringvector_property(CdlInterpreter, int, char**, std::string, + char**, void (*)(CdlInterpreter, CdlProperty_StringVector)); + static int parse_reference_property(CdlInterpreter, int, char**, std::string, + char**, void (*)(CdlInterpreter, CdlProperty_Reference), + CdlUpdateHandler); + static int parse_expression_property(CdlInterpreter, int, char**, std::string, + char **, void (*)(CdlInterpreter, CdlProperty_Expression), + CdlUpdateHandler); + static int parse_listexpression_property(CdlInterpreter, int, char**, std::string, + char **, void (*)(CdlInterpreter, CdlProperty_ListExpression), + CdlUpdateHandler); + static int parse_goalexpression_property(CdlInterpreter, int, 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) throw(CdlInputOutputException, std::bad_alloc); + 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) throw(CdlInputOutputException, std::bad_alloc); + + 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) + throw(CdlInputOutputException, std::bad_alloc); + void get_config_headers(std::vector<std::string>&); + void generate_build_tree(std::string, std::string = "") + throw(CdlInputOutputException, std::bad_alloc); + + // 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) + throw(CdlInputOutputException, std::bad_alloc); + static int savefile_handle_command(CdlInterpreter, int, char**); + static int savefile_handle_unsupported(CdlInterpreter, int, char**); + static int savefile_handle_unknown(CdlInterpreter, int, char**); + void save_unsupported_commands(CdlInterpreter, Tcl_Channel, int, bool) + throw(CdlInputOutputException, std::bad_alloc); + static cdl_int get_library_savefile_version(); + static int savefile_handle_version(CdlInterpreter, int, char**); + static cdl_int get_savefile_version(CdlInterpreter); + void save_conflicts(CdlInterpreter, Tcl_Channel, int, bool) throw(CdlInputOutputException, std::bad_alloc); + static void save_separator(CdlInterpreter, Tcl_Channel, std::string, bool) + throw(CdlInputOutputException, std::bad_alloc); + + 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, char**); + static int parse_display(CdlInterpreter, int, char**); + static int parse_doc(CdlInterpreter, int, 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) throw(CdlInputOutputException, std::bad_alloc); + + 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, 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, char**); + static void active_if_update_handler(CdlTransaction, CdlNode, CdlProperty, CdlNode, CdlUpdate); + static int parse_calculated(CdlInterpreter, int, char**); + static void calculated_update_handler(CdlTransaction, CdlNode, CdlProperty, CdlNode, CdlUpdate); + static int parse_check_proc(CdlInterpreter, int, char**); + static int parse_default_value(CdlInterpreter, int, char**); + static void default_value_update_handler(CdlTransaction, CdlNode, CdlProperty, CdlNode, CdlUpdate); + static int parse_dialog(CdlInterpreter, int, char**); + static void dialog_update_handler(CdlTransaction, CdlNode, CdlProperty, CdlNode, CdlUpdate); + static int parse_entry_proc(CdlInterpreter, int, char**); + static int parse_flavor(CdlInterpreter, int, char**); + static int parse_group(CdlInterpreter, int, char**); + static int parse_implements(CdlInterpreter, int, char**); + static void implements_update_handler(CdlTransaction, CdlNode, CdlProperty, CdlNode, CdlUpdate); + static int parse_legal_values(CdlInterpreter, int, char**); + static void legal_values_update_handler(CdlTransaction, CdlNode, CdlProperty, CdlNode, CdlUpdate); + static int parse_requires(CdlInterpreter, int, char**); + static void requires_update_handler(CdlTransaction, CdlNode, CdlProperty, CdlNode, CdlUpdate); + static int parse_wizard(CdlInterpreter, int, char**); + static void wizard_update_handler(CdlTransaction, CdlNode, CdlProperty, CdlNode, CdlUpdate); + + // Persistence suppot + void save(CdlInterpreter, Tcl_Channel, int, bool /* modifiable */, bool /* minimal */) + throw(CdlInputOutputException, std::bad_alloc); + bool value_savefile_entry_needed() const; + static void initialize_savefile_support(CdlToplevel, std::string); + static int savefile_value_source_command(CdlInterpreter, int, char**); + static int savefile_user_value_command(CdlInterpreter, int, char**); + static int savefile_wizard_value_command(CdlInterpreter, int, char**); + static int savefile_inferred_value_command(CdlInterpreter, int, char**); + static int savefile_xxx_value_command(CdlInterpreter, int, 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(); + ~CdlTransactionCallback(); + static void (*get_callback_fn())(const CdlTransactionCallback&); + static void set_callback_fn(void (*)(const CdlTransactionCallback&)); + + // 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: + + 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) +// throw(CdlInputOutputException, std::bad_alloc); +// +// 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, char**); + static int parse_makefile(CdlInterpreter, int, char**); + static int parse_include_dir(CdlInterpreter, int, char**); + static int parse_include_files(CdlInterpreter, int, 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, char**); + static int parse_compile(CdlInterpreter, int, char**); + static int parse_make(CdlInterpreter, int, char**); + static int parse_make_object(CdlInterpreter, int, char**); + static int parse_object(CdlInterpreter, int, 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 + throw(CdlInputOutputException, std::bad_alloc); + + // 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, 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 + throw(CdlInputOutputException, std::bad_alloc); + + // 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, char**); + static int parse_define_format(CdlInterpreter, int, char**); + static int parse_define_proc(CdlInterpreter, int, char**); + static int parse_if_define(CdlInterpreter, int, char**); + static int parse_no_define(CdlInterpreter, int, 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, char**); + static int parse_display_proc(CdlInterpreter, int, char**); + static int parse_update_proc(CdlInterpreter, int, char**); + + // Persistence support. Dialogs should just be ignored when it + // comes to saving and restoring files. + virtual void save(CdlInterpreter, Tcl_Channel, int, bool) throw(CdlInputOutputException, std::bad_alloc); + + 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, char**); + static int parse_cancel_proc(CdlInterpreter, int, char**); + static int parse_confirm_proc(CdlInterpreter, int, char**); + static int parse_decoration_proc(CdlInterpreter, int, char**); + static int parse_init_proc(CdlInterpreter, int, char**); + static int parse_screen(CdlInterpreter, int, char**); + + // Persistence support. Wizards should just be ignored when it + // comes to saving and restoring files. + virtual void save(CdlInterpreter, Tcl_Channel, int, bool) throw(CdlInputOutputException, std::bad_alloc); + + 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, 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) throw(CdlInputOutputException, std::bad_alloc); + static void initialize_savefile_support(CdlToplevel); + static int savefile_interface_command(CdlInterpreter, int, 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
