3 //============================================================================
7 // Implementation of the CdlConfiguration class
9 //============================================================================
10 //####COPYRIGHTBEGIN####
12 // ----------------------------------------------------------------------------
13 // Copyright (C) 2002 Bart Veer
14 // Copyright (C) 1999, 2000 Red Hat, Inc.
16 // This file is part of the eCos host tools.
18 // This program is free software; you can redistribute it and/or modify it
19 // under the terms of the GNU General Public License as published by the Free
20 // Software Foundation; either version 2 of the License, or (at your option)
23 // This program is distributed in the hope that it will be useful, but WITHOUT
24 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
25 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
28 // You should have received a copy of the GNU General Public License along with
29 // this program; if not, write to the Free Software Foundation, Inc.,
30 // 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
32 // ----------------------------------------------------------------------------
34 //####COPYRIGHTEND####
35 //============================================================================
36 //#####DESCRIPTIONBEGIN####
43 //####DESCRIPTIONEND####
44 //============================================================================
49 // ----------------------------------------------------------------------------
50 #include "cdlconfig.h"
52 // Get the infrastructure types, assertions, tracing and similar
54 #include <cyg/infra/cyg_ass.h>
55 #include <cyg/infra/cyg_trac.h>
57 // <cdl.hxx> defines everything implemented in this module.
58 // It implicitly supplies <string>, <vector> and <map> because
59 // the class definitions rely on these headers.
64 //{{{ CdlConfiguration constants and statics
66 // ----------------------------------------------------------------------------
67 CYGDBG_DEFINE_MEMLEAK_COUNTER(CdlConfigurationBody);
70 //{{{ CdlConfiguration:: creation
72 // ----------------------------------------------------------------------------
73 // The toplevel class will take care of just about everything.
75 CdlConfigurationBody::CdlConfigurationBody(std::string name, CdlPackagesDatabase db, CdlInterpreter interp)
77 CdlToplevelBody(interp, db->get_component_repository())
79 CYG_REPORT_FUNCNAME("CdlConfiguration:: constructor");
80 CYG_REPORT_FUNCARG1XV(this);
82 current_hardware = "";
83 current_template = "";
88 cdlconfigurationbody_cookie = CdlConfigurationBody_Magic;
89 CYGDBG_MEMLEAK_CONSTRUCTOR();
91 CYG_POSTCONDITION_THISC();
95 // ----------------------------------------------------------------------------
96 // The exported interface
99 CdlConfigurationBody::make(std::string name, CdlPackagesDatabase db, CdlInterpreter interp)
101 CYG_REPORT_FUNCNAMETYPE("CdlConfiguration::make", "result %p");
102 CYG_REPORT_FUNCARG2XV(db, interp);
103 CYG_PRECONDITIONC("" != name);
104 CYG_PRECONDITION_CLASSC(db);
105 CYG_PRECONDITION_CLASSC(interp);
107 CdlConfiguration result = new CdlConfigurationBody(name, db, interp);
108 CYG_REPORT_RETVAL(result);
113 //{{{ CdlConfiguration:: destructor
115 // ----------------------------------------------------------------------------
116 CdlConfigurationBody::~CdlConfigurationBody()
118 CYG_REPORT_FUNCNAME("CdlConfiguration:: default destructor");
119 CYG_REPORT_FUNCARG1("this %p", this);
120 CYG_PRECONDITION_THISC();
122 // Removing all the packages has to happen here, and in the
123 // context of a transaction. The main reason is the extensive
124 // use of dynamic casts, after this destructor returns
125 // any dynamic casts for this configuration will fail.
127 // Arguably some of the unloads should happen by clearing
128 // the hardware and template (assuming those are currently
129 // set). In practice that would not really gain anything.
131 // Unloading the individual packages is a bit more expensive
132 // than it should be, since lots of care is taken to keep
133 // remaining packages consistent and then those get unloaded
134 // as well. However it is the safe approach.
135 CdlLocalTransaction transaction(this);
136 const std::vector<CdlLoadable>& loadables = this->get_loadables();
137 for (int i = loadables.size() - 1; i >= 0; i--) {
138 CdlPackage pkg = dynamic_cast<CdlPackage>(loadables[i]);
140 this->unload_package(transaction.get(), pkg);
143 transaction.propagate();
144 transaction.commit();
146 cdlconfigurationbody_cookie = CdlConfigurationBody_Invalid;
147 current_hardware = "";
148 current_template = "";
152 CYGDBG_MEMLEAK_DESTRUCTOR();
158 //{{{ CdlConfiguration::check_this()
160 // ----------------------------------------------------------------------------
161 // There is very little information associated with a configuration.
164 CdlConfigurationBody::check_this(cyg_assert_class_zeal zeal) const
166 if (CdlConfigurationBody_Magic != cdlconfigurationbody_cookie) {
169 CYGDBG_MEMLEAK_CHECKTHIS();
172 case cyg_system_test :
174 if ((0 == database) || !database->check_this(cyg_quick)) {
178 if (("" != current_hardware) && !database->is_known_target(current_hardware)) {
181 if (("" != current_template) && !database->is_known_template(current_template)) {
194 return CdlNodeBody::check_this(zeal) && CdlContainerBody::check_this(zeal) && CdlToplevelBody::check_this(zeal);
198 //{{{ CdlConfiguration:: basic info
200 // ----------------------------------------------------------------------------
201 // Provide ready access to configuration-specific data.
204 CdlConfigurationBody::get_database() const
206 CYG_REPORT_FUNCNAMETYPE("CdlConfiguration::get_database", "result %p");
207 CYG_REPORT_FUNCARG1XV(this);
208 CYG_PRECONDITION_THISC();
210 CYG_REPORT_RETVAL(database);
215 CdlConfigurationBody::get_hardware() const
217 CYG_REPORT_FUNCNAME("CdlConfiguration::get_hardware");
218 CYG_REPORT_FUNCARG1XV(this);
219 CYG_PRECONDITION_THISC();
222 return current_hardware;
226 CdlConfigurationBody::set_hardware_name(std::string new_name)
228 CYG_REPORT_FUNCNAME("CdlConfiguration::set_hardware_name");
229 CYG_REPORT_FUNCARG1XV(this);
230 CYG_PRECONDITION_THISC();
232 current_hardware = new_name;
238 CdlConfigurationBody::get_template() const
240 CYG_REPORT_FUNCNAME("CdlConfiguration::get_template");
241 CYG_REPORT_FUNCARG1XV(this);
242 CYG_PRECONDITION_THISC();
245 return current_template;
249 CdlConfigurationBody::set_template_name(std::string new_name)
251 CYG_REPORT_FUNCNAME("CdlConfiguration::set_template_name");
252 CYG_REPORT_FUNCARG1XV(this);
253 CYG_PRECONDITION_THISC();
255 current_template = new_name;
261 CdlConfigurationBody::get_save_file() const
263 CYG_REPORT_FUNCNAME("CdlConfiguration::get_save_file");
264 CYG_REPORT_FUNCARG1XV(this);
265 CYG_PRECONDITION_THISC();
271 // ----------------------------------------------------------------------------
274 CdlConfigurationBody::get_class_name() const
276 CYG_REPORT_FUNCNAME("CdlConfiguration::get_class_name");
277 CYG_PRECONDITION_THISC();
279 return "CdlConfiguration";
283 //{{{ Load and unload operations - wrappers
285 // ----------------------------------------------------------------------------
286 // These members are basically wrappers for the functions that do the
287 // real work. They do things like running the real functions inside
288 // a newly created transaction.
291 CdlConfigurationBody::load_package(std::string name, std::string version,
292 CdlDiagnosticFnPtr error_fn, CdlDiagnosticFnPtr warn_fn, bool limbo)
294 CYG_REPORT_FUNCNAME("CdlConfiguration::load_package");
295 CYG_REPORT_FUNCARG1XV(this);
296 CYG_PRECONDITION_THISC();
298 CdlLocalTransaction transaction(this);
299 this->load_package(transaction.get(), name, version, error_fn, warn_fn, limbo);
306 CdlConfigurationBody::unload_package(std::string name, bool limbo)
308 CYG_REPORT_FUNCNAME("CdlConfiguration::unload_package");
309 CYG_REPORT_FUNCARG1XV(this);
310 CYG_PRECONDITION_THISC();
312 CdlLocalTransaction transaction(this);
313 this->unload_package(transaction.get(), name, limbo);
320 CdlConfigurationBody::unload_package(CdlPackage package, bool limbo)
322 CYG_REPORT_FUNCNAME("CdlConfiguration::unload_package");
323 CYG_REPORT_FUNCARG1XV(this);
325 CdlLocalTransaction transaction(this);
326 this->unload_package(transaction.get(), package, limbo);
333 CdlConfigurationBody::unload_package(CdlTransaction transaction, std::string name, bool limbo)
335 CYG_REPORT_FUNCNAME("CdlConfiguration::unload_package");
336 CYG_REPORT_FUNCARG3XV(this, transaction, limbo);
337 CYG_INVARIANT_THISC(CdlConfigurationBody);
338 CYG_PRECONDITION_CLASSC(transaction);
340 CdlNode node = lookup(name);
341 CYG_ASSERTC(0 != node);
342 CdlPackage package = dynamic_cast<CdlPackage>(node);
343 CYG_ASSERTC(0 != package);
345 this->unload_package(transaction, package, limbo);
351 CdlConfigurationBody::change_package_version(std::string name, std::string version,
352 CdlDiagnosticFnPtr error_fn, CdlDiagnosticFnPtr warn_fn, bool limbo)
354 CYG_REPORT_FUNCNAME("CdlConfiguration::change_package_version");
355 CYG_REPORT_FUNCARG1XV(this);
357 CdlLocalTransaction transaction(this);
358 this->change_package_version(transaction.get(), name, version, error_fn, warn_fn, limbo);
365 CdlConfigurationBody::change_package_version(CdlPackage package, std::string version,
366 CdlDiagnosticFnPtr error_fn, CdlDiagnosticFnPtr warn_fn, bool limbo)
368 CYG_REPORT_FUNCNAME("CdlConfiguration::change_package_version");
369 CYG_REPORT_FUNCARG1XV(this);
371 CdlLocalTransaction transaction(this);
372 this->change_package_version(transaction.get(), package, version, error_fn, warn_fn, limbo);
379 CdlConfigurationBody::change_package_version(CdlTransaction transaction, std::string name, std::string new_version,
380 CdlDiagnosticFnPtr error_fn, CdlDiagnosticFnPtr warn_fn, bool limbo)
382 CYG_REPORT_FUNCNAME("CdlConfiguration::change_package_version");
383 CYG_REPORT_FUNCARG2XV(this, transaction);
384 CYG_PRECONDITION_THISC();
385 CYG_PRECONDITION_CLASSC(transaction);
386 CYG_PRECONDITIONC("" != name);
388 CdlPackage package = 0;
389 CdlNode node = this->lookup(name);
391 package = dynamic_cast<CdlPackage>(node);
393 // For now it is illegal to change the version of package that has
394 // not been loaded yet
396 throw CdlInputOutputException(std::string("Cannot change version of \"") + name + "\" , this package is not loaded");
398 CYG_ASSERT_CLASSC(package);
400 this->change_package_version(transaction, package, new_version, error_fn, warn_fn, limbo);
406 CdlConfigurationBody::add(std::string filename,
407 CdlDiagnosticFnPtr error_fn, CdlDiagnosticFnPtr warn_fn)
409 CYG_REPORT_FUNCNAME("CdlConfiguration::add");
410 CYG_REPORT_FUNCARG1XV(this);
412 CdlLocalTransaction transaction(this);
413 this->add(transaction.get(), filename, error_fn, warn_fn);
420 CdlConfigurationBody::set_template(std::string name, std::string version,
421 CdlDiagnosticFnPtr error_fn, CdlDiagnosticFnPtr warn_fn, bool limbo)
423 CYG_REPORT_FUNCNAME("CdlConfiguration::set_template");
424 CYG_REPORT_FUNCARG1XV(this);
426 CdlLocalTransaction transaction(this);
427 this->set_template(transaction.get(), name, version, error_fn, warn_fn, limbo);
434 CdlConfigurationBody::set_template_file(std::string filename,
435 CdlDiagnosticFnPtr error_fn, CdlDiagnosticFnPtr warn_fn, bool limbo)
437 CYG_REPORT_FUNCNAME("CdlConfiguration::set_template_file");
438 CYG_REPORT_FUNCARG1XV(this);
440 CdlLocalTransaction transaction(this);
441 this->set_template_file(transaction.get(), filename, error_fn, warn_fn, limbo);
448 CdlConfigurationBody::set_template(CdlTransaction transaction, std::string template_name, std::string version,
449 CdlDiagnosticFnPtr error_fn, CdlDiagnosticFnPtr warn_fn, bool limbo)
451 CYG_REPORT_FUNCNAME("CdlConfiguration::set_template");
452 CYG_REPORT_FUNCARG2XV(this, transaction);
454 // Some consistency checks before doing anything damaging
455 if (!this->database->is_known_template(template_name)) {
456 throw CdlInputOutputException("Unknown template " + template_name);
458 std::string template_filename = this->database->get_template_filename(template_name, version);
459 if ("" == template_filename) {
461 throw CdlInputOutputException("There is no template file corresponding to " + template_name);
463 throw CdlInputOutputException("There is no temmplate file corresponding to version "
464 + version + " of " + template_name);
468 // Now use set_template_file() to do the hard work.
469 this->set_template_file(transaction, template_filename, error_fn, warn_fn, limbo);
470 current_template = template_name;
476 CdlConfigurationBody::unload_template(bool limbo)
478 CYG_REPORT_FUNCNAME("CdlConfiguration::unload_template");
479 CYG_REPORT_FUNCARG1XV(this);
481 CdlLocalTransaction transaction(this);
482 this->unload_template(transaction.get(), limbo);
489 CdlConfigurationBody::set_hardware(std::string name,
490 CdlDiagnosticFnPtr error_fn, CdlDiagnosticFnPtr warn_fn, bool limbo)
492 CYG_REPORT_FUNCNAME("CdlConfiguration::set_hardware");
493 CYG_REPORT_FUNCARG1XV(this);
495 CdlLocalTransaction transaction(this);
496 this->set_hardware(transaction.get(), name, error_fn, warn_fn, limbo);
503 CdlConfigurationBody::unload_hardware(bool limbo)
505 CYG_REPORT_FUNCNAME("CdlConfiguration::unload_hardware");
506 CYG_REPORT_FUNCARG1XV(this);
508 CdlLocalTransaction transaction(this);
509 this->unload_hardware(transaction.get(), limbo);
516 //{{{ Load and unload - transaction support
518 // ----------------------------------------------------------------------------
519 // A number of commit/cancel auxiliary classes are needed to allow the
520 // load/unload code to integrate properly with the transaction code.
522 class CdlConfiguration_CommitCancelLoad :
523 public CdlTransactionCommitCancelOp
525 friend class CdlTest;
529 CdlConfiguration_CommitCancelLoad(CdlPackage package_arg)
530 : CdlTransactionCommitCancelOp()
532 CYG_ASSERT_CLASSC(package_arg);
533 package = package_arg;
535 ~CdlConfiguration_CommitCancelLoad()
539 void commit(CdlTransaction transaction)
541 CYG_ASSERT_CLASSC(package);
542 CdlLoadableBody::transaction_commit_load(transaction, package);
545 void cancel(CdlTransaction transaction)
547 CYG_ASSERT_CLASSC(package);
548 CdlLoadableBody::transaction_cancel_load(transaction, package);
555 CdlConfiguration_CommitCancelLoad()
561 class CdlConfiguration_CommitCancelUnload :
562 public CdlTransactionCommitCancelOp
564 friend class CdlTest;
567 CdlConfiguration_CommitCancelUnload(CdlPackage package_arg)
568 : CdlTransactionCommitCancelOp()
570 CYG_ASSERT_CLASSC(package_arg);
571 package = package_arg;
573 ~CdlConfiguration_CommitCancelUnload()
577 void commit(CdlTransaction transaction)
579 CYG_PRECONDITION_CLASSC(package);
580 CdlLoadableBody::transaction_commit_unload(transaction, package);
583 void cancel(CdlTransaction transaction)
585 CYG_PRECONDITION_CLASSC(package);
586 CdlLoadableBody::transaction_cancel_unload(transaction, package);
593 CdlConfiguration_CommitCancelUnload()
599 // These utility classes can be used to control the hardware and
600 // template names. If the transaction is cancelled the previous
601 // name gets re-installed
602 class CdlConfiguration_CommitCancelHardwareName :
603 public CdlTransactionCommitCancelOp
605 friend class CdlTest;
608 CdlConfiguration_CommitCancelHardwareName(std::string old_name_arg)
609 : CdlTransactionCommitCancelOp()
611 old_name = old_name_arg;
613 ~CdlConfiguration_CommitCancelHardwareName()
617 void commit(CdlTransaction transaction)
619 // The new name is already installed, nothing more needs to happen.
620 CYG_UNUSED_PARAM(CdlTransaction, transaction);
622 void cancel(CdlTransaction transaction)
624 // Restore the old name
625 CdlToplevel toplevel = transaction->get_toplevel();
626 CYG_ASSERTC(0 != toplevel);
627 CdlConfiguration configuration = dynamic_cast<CdlConfiguration>(toplevel);
628 CYG_ASSERT_CLASSC(configuration);
630 configuration->set_hardware_name(old_name);
631 CYG_UNUSED_PARAM(CdlTransaction, transaction);
637 CdlConfiguration_CommitCancelHardwareName()
640 std::string old_name;
643 class CdlConfiguration_CommitCancelTemplateName :
644 public CdlTransactionCommitCancelOp
646 friend class CdlTest;
649 CdlConfiguration_CommitCancelTemplateName(std::string old_name_arg)
650 : CdlTransactionCommitCancelOp()
652 old_name = old_name_arg;
654 ~CdlConfiguration_CommitCancelTemplateName()
658 void commit(CdlTransaction transaction)
660 // The new name is already installed, nothing more needs to happen.
661 CYG_UNUSED_PARAM(CdlTransaction, transaction);
663 void cancel(CdlTransaction transaction)
665 // Restore the old name
666 CdlToplevel toplevel = transaction->get_toplevel();
667 CYG_ASSERTC(0 != toplevel);
668 CdlConfiguration configuration = dynamic_cast<CdlConfiguration>(toplevel);
669 CYG_ASSERT_CLASSC(configuration);
671 configuration->set_template_name(old_name);
672 CYG_UNUSED_PARAM(CdlTransaction, transaction);
678 CdlConfiguration_CommitCancelTemplateName()
681 std::string old_name;
685 //{{{ CdlConfiguration::load_package()
687 // ----------------------------------------------------------------------------
688 // Loading a package into the current level. This involves the following
691 // 1) check that the specified package name and version is valid, by
692 // comparing it with the database. When the database was created there
693 // will have been checks to make sure that the initial CDL script was
694 // present, but more checks can be done here.
696 // 2) before allocating any resources, check that there is no name conflict
697 // for the package itself.
699 // 3) create the package object, and add it to the toplevel of the current
700 // configuration. It may get reparented later on. Part of the creation
701 // process is to allocate a new slave interpreter, which can be updated
702 // with various bits of information.
704 // 4) evaluate the toplevel script. Subsidiary component scripts will
705 // get evaluated as a side effect. The various nodes will be added
706 // to the hierarchy as they are created, but no property binding
709 // Any failure up to this point should result in the entire package
710 // being removed from the hierarchy and then destroyed, thus leaving
711 // the configuration in its original state.
713 // 5) now property binding needs to take place. This can have lots
714 // of side effects, e.g. default values may get calculated, the
715 // hierarchy may change because of parent properties, etc.
716 // The work is done inside CdlLoadable::bind() which will undo
717 // everything on failure - although bad_alloc is the only
718 // failure that should occur.
720 // 6) load operations can get cancelled, so a suitable commit/cancel
721 // operation needs to allocated and added to the transaction.
723 // 7) if limbo is enabled, previous values should be extracted from
724 // limbo if at all possible. In addition the package's value can
725 // be set to its version.
728 CdlConfigurationBody::load_package(CdlTransaction transaction, std::string name, std::string version,
729 CdlDiagnosticFnPtr error_fn, CdlDiagnosticFnPtr warn_fn, bool limbo)
731 CYG_REPORT_FUNCNAME("CdlConfiguration::load_package");
732 CYG_REPORT_FUNCARG1XV(this);
733 CYG_PRECONDITION_THISC();
734 CYG_PRECONDITION_CLASSC(transaction);
735 CYG_PRECONDITIONC("" != name);
737 // Locate the database entry. Also check the version (filling it in if necessary).
738 // Get hold of the package directory and the initial script.
739 if (!database->is_known_package(name)) {
740 throw CdlInputOutputException("Unknown package " + name);
742 const std::vector<std::string>& versions = database->get_package_versions(name);
744 version = *(versions.begin());
746 if (std::find(versions.begin(), versions.end(), version) == versions.end()) {
747 throw CdlInputOutputException("Package " + name + " does not have an installed version `" + version + "'.");
750 std::string directory = database->get_package_directory(name);
751 std::string script = database->get_package_script(name);
752 CYG_ASSERTC(("" != directory) && ("" != script));
754 // Check that the directory actually exists. For this the configuration's own
755 // interpreter can be used.
756 CdlInterpreter interp = get_interpreter();
757 CYG_ASSERT_CLASSC(interp);
759 std::string tcl_cmd = "regsub -all -- {\\\\} [file join " + directory + " " + version + "] / result; return $result";
760 std::string tcl_result;
761 if (TCL_OK != interp->eval(tcl_cmd, tcl_result)) {
762 throw CdlInputOutputException("Cannot load package `" + name + "', internal error constructing pathname.");
764 directory = tcl_result;
766 tcl_cmd = "file isdirectory [file join \"" + database->get_component_repository() + "\" " + directory + "]";
767 if ((TCL_OK != interp->eval(tcl_cmd, tcl_result)) || ("1" != tcl_result)) {
768 throw CdlInputOutputException("Cannot load package `" + name + "', there is no directory `" + directory + "'.");
771 // Make sure that there is no name conflict. No resources have been allocated
772 // yet, so this is a good time.
773 CdlNode node = lookup(name);
775 if (0 != dynamic_cast<CdlPackage>(node)) {
776 throw CdlInputOutputException("Package `" + name + "' is already loaded.");
779 std::string msg = "Name clash for package `" + name + "',there is a `" +
780 node->get_class_name() + " " + name + "' already loaded";
781 CdlLoadable owner_pkg = node->get_owner();
782 if (0 != owner_pkg) {
783 msg += " in package " + owner_pkg->get_name();
785 throw CdlInputOutputException(msg);
789 // Now create the package object itself.
790 CdlPackage package = 0;
792 CdlConfiguration_CommitCancelLoad* load_op = 0;
795 package = new CdlPackageBody(name, this, directory);
797 // The package should be added to the hierarchy immediately.
798 // All nodes will get added to the hierarchy as they are
799 // created, an operation that has to be undone during
801 this->add_node(package, this, package);
803 // Load the package data. The various nodes will all end up
804 // in a hierarchy below the package, but without any checks
805 // for name conflicts etc and ignoring any re-parenting.
806 CdlInterpreter interp = package->get_interpreter();
807 CYG_ASSERT_CLASSC(interp);
809 interp->add_command("unknown", &CdlParse::unknown_command);
810 CdlInterpreterBody::DiagSupport diag_support(interp, error_fn, warn_fn);
812 // Next figure out the script name, and make sure that it exists.
813 std::string actual_script = package->find_absolute_file(script, "cdl");
814 if ("" == actual_script) {
815 throw CdlInputOutputException("Package " + name + ", unable to find initial script " + script);
817 tcl_cmd = "file isfile \"" + actual_script + "\"";
818 if ((TCL_OK != interp->eval(tcl_cmd, tcl_result)) || ("1" != tcl_result)) {
819 throw CdlInputOutputException("Package " + name + ", " + actual_script + " is not a CDL script");
822 // The script is valid. Set up the interpreter appropriately.
823 CdlParse::clear_error_count(interp);
824 static CdlInterpreterCommandEntry commands[] =
826 CdlInterpreterCommandEntry("cdl_package", &CdlPackageBody::parse_package ),
827 CdlInterpreterCommandEntry("cdl_component", &CdlComponentBody::parse_component ),
828 CdlInterpreterCommandEntry("cdl_option", &CdlOptionBody::parse_option ),
829 CdlInterpreterCommandEntry("cdl_interface", &CdlInterfaceBody::parse_interface ),
830 CdlInterpreterCommandEntry("cdl_dialog", &CdlDialogBody::parse_dialog ),
831 CdlInterpreterCommandEntry("cdl_wizard", &CdlWizardBody::parse_wizard ),
832 CdlInterpreterCommandEntry("", 0 )
834 CdlInterpreterBody::CommandSupport interp_cmds(interp, commands);
835 CdlInterpreterBody::ContainerSupport interp_container(interp, package);
836 CdlInterpreterBody::ContextSupport interp_context(interp, actual_script);
838 // The interpreter is now ready.
839 (void) interp->eval_file(actual_script);
841 // Clean out the commands etc. This interpreter may get used again
842 // in future, and it should not be possible to define new options
843 // etc. in that invocation.
844 interp->remove_command("unknown");
846 // All the data has been read in without generating an
847 // exception. However there may have been errors reported via
848 // the parse_error_fn, and any errors at all should result
850 int error_count = CdlParse::get_error_count(interp);
851 if (error_count > 0) {
853 Cdl::integer_to_string(error_count, tmp);
854 throw CdlParseException("Package " + name + ", " + tmp + " error" +
855 ((error_count > 1) ? "s" : "") +
856 " occurred while reading in the CDL data.");
859 // All the data has been read in, implying that there are no
860 // fatal problems with the data. Now try to bind all
861 // references to and from this loadable.
862 package->bind(transaction);
865 // Finally, create a suitable transaction commit/cancel object
866 // and add it to the transaction.
867 load_op = new CdlConfiguration_CommitCancelLoad(package);
868 transaction->add_commit_cancel_op(load_op);
872 // Something went wrong during the create or load. It is necessary
873 // to delete the package. Undo all the operations above, in
874 // reverse order. The add_commit_cancel_op() was the last step,
875 // so need not be undone here.
881 // Note: no attempt is made to recover from errors here
883 package->unbind(transaction);
885 this->remove_loadable_from_toplevel(package);
891 // FIXME: implement limbo support
893 // We also have a sensible value for the package as a whole.
894 // Use this value for both default and user - after all the
895 // user has selected the package.
896 package->enable_and_set_value(transaction, version, CdlValueSource_Default);
897 package->enable_and_set_value(transaction, version, CdlValueSource_User);
903 //{{{ CdlConfiguration::unload_package()
905 // ----------------------------------------------------------------------------
906 // Unloading a package is very simple. If requested, save all current
907 // values to limbo: there is no point in saving default values, these
908 // will get recalculated from the default_value property anyway;
909 // inferred values should be saved, there is no guarantee that the exact
910 // same value will be calculated again, and if the inferred value is no
911 // longer correct then the inference engine can freely update it.
913 // Next, unbind the package and remove it from the hierarchy. These
914 // operations are reversible if the transaction gets cancelled.
915 // A suitable transaction commit/cancel object is created and
916 // added to the transaction.
918 CdlConfigurationBody::unload_package(CdlTransaction transaction, CdlPackage package, bool limbo)
920 CYG_REPORT_FUNCNAME("CdlConfiguration::unload_package");
921 CYG_REPORT_FUNCARG4XV(this, transaction, package, limbo);
922 CYG_INVARIANT_THISC(CdlConfigurationBody);
923 CYG_INVARIANT_CLASSC(CdlTransactionBody, transaction);
924 CYG_PRECONDITION_CLASSC(package);
927 const std::vector<CdlNode>& pkg_contents = package->get_owned();
928 std::vector<CdlNode>::const_iterator node_i;
930 for (node_i = pkg_contents.begin(); node_i != pkg_contents.end(); node_i++) {
931 CdlValuable valuable = dynamic_cast<CdlValuable>(*node_i);
933 if (valuable->has_source(CdlValueSource_Inferred) ||
934 valuable->has_source(CdlValueSource_Wizard) ||
935 valuable->has_source(CdlValueSource_User)) {
937 set_limbo_value(valuable);
943 bool unbound = false;
944 bool removed = false;
945 CdlConfiguration_CommitCancelUnload* unload_op = 0;
948 package->unbind(transaction);
950 this->remove_loadable_from_toplevel(package);
952 unload_op = new CdlConfiguration_CommitCancelUnload(package);
953 transaction->add_commit_cancel_op(unload_op);
956 if (0 != unload_op) {
960 this->add_loadable_to_toplevel(package);
963 package->bind(transaction);
972 //{{{ CdlConfiguration::change_package_version()
974 // ----------------------------------------------------------------------------
975 // Changing a package version is just a case of unloading the old version
976 // and then loading in the new version. Because this all happens in the
977 // context of a transaction it is possible to undo the unload on
978 // failure, and the whole transaction can be cancelled at a higher level.
981 CdlConfigurationBody::change_package_version(CdlTransaction transaction, CdlPackage package, std::string new_version,
982 CdlDiagnosticFnPtr error_fn, CdlDiagnosticFnPtr warn_fn, bool limbo)
984 CYG_REPORT_FUNCNAME("CdlConfiguration::change_package_version");
985 CYG_REPORT_FUNCARG3XV(this, transaction, package);
986 CYG_PRECONDITION_THISC();
987 CYG_INVARIANT_CLASSC(CdlTransactionBody, transaction);
988 CYG_PRECONDITION_CLASSC(package);
989 // "" is valid for the version, it indicates the default
991 // Since the package is already loaded it must be in the database,
992 // but it is possible that the desired version does not exist.
993 std::string name = package->get_name();
994 const std::vector<std::string>& pkg_versions = database->get_package_versions(name);
995 if ("" == new_version) {
996 new_version = *(pkg_versions.begin());
997 } else if (std::find(pkg_versions.begin(), pkg_versions.end(), new_version) == pkg_versions.end()) {
998 throw CdlInputOutputException("Version " + new_version + " of package " + name + " is not installed.");
1001 bool unloaded = false;
1003 this->unload_package(transaction, package, limbo);
1005 this->load_package(transaction, name, new_version, error_fn, warn_fn, limbo);
1008 // There should be a commit/cancel op for the unload package step.
1009 // This can be undone.
1010 CdlTransactionCommitCancelOp* unload_op = transaction->get_last_commit_cancel_op();
1011 CYG_ASSERTC(0 != unload_op);
1012 CYG_ASSERTC(0 != dynamic_cast<CdlConfiguration_CommitCancelUnload*>(unload_op));
1013 transaction->cancel_last_commit_cancel_op();
1014 CYG_UNUSED_PARAM(CdlTransactionCommitCancelOp*, unload_op);
1019 CYG_REPORT_RETURN();
1023 //{{{ CdlConfiguration::set_hardware() etc.
1025 // ----------------------------------------------------------------------------
1026 // Setting the hardware involves unloading the old hardware, if any, and
1027 // then loading in the new one. Obviously this should only happen if
1028 // the new hardware name is valid. It would be possible to optimise for
1029 // the case where the old and new hardware are the same, subject
1030 // to dynamic database reload support.
1033 CdlConfigurationBody::set_hardware(CdlTransaction transaction, std::string target_name,
1034 CdlDiagnosticFnPtr error_fn, CdlDiagnosticFnPtr warn_fn, bool limbo)
1036 CYG_REPORT_FUNCNAME("CdlConfiguration::set_hardware");
1037 CYG_REPORT_FUNCARG2XV(this, transaction);
1038 CYG_PRECONDITION_THISC();
1039 CYG_PRECONDITION_CLASSC(transaction);
1041 // Minimal consistency check before attempting anything complicated.
1042 if (!database->is_known_target(target_name)) {
1043 throw CdlInputOutputException("Unknown target " + target_name);
1046 CdlInterpreter interp = this->get_interpreter();
1047 CdlInterpreterBody::DiagSupport diag_support(interp, error_fn, warn_fn);
1048 CdlInterpreterBody::ContextSupport context_support(interp, "Hardware selection");
1050 CdlConfiguration_CommitCancelHardwareName* rename_op = new CdlConfiguration_CommitCancelHardwareName(current_hardware);
1052 transaction->add_commit_cancel_op(rename_op);
1053 const std::vector<CdlLoadable>& loadables = this->get_loadables();
1055 for (i = (int) loadables.size() - 1; i >= 0; i--) {
1056 CdlPackage package = dynamic_cast<CdlPackage>(loadables[i]);
1057 if ((0 != package) && package->belongs_to_hardware()) {
1058 this->unload_package(transaction, package, limbo);
1061 current_hardware = "";
1063 if ("" != target_name) {
1065 const std::vector<std::string>& packages = database->get_target_packages(target_name);
1066 std::vector<std::string>::const_iterator name_i;
1067 for (name_i = packages.begin(); name_i != packages.end(); name_i++) {
1068 // Target specifications may refer to packages that are not
1069 // installed. This is useful in e.g. an anoncvs environment.
1070 if (database->is_known_package(*name_i)) {
1071 // It is possible for a hardware package to have been
1072 // loaded separately, in which case there is no point in
1073 // loading it again.
1074 if (0 == this->lookup(*name_i)) {
1075 this->load_package(transaction, *name_i, "",
1076 error_fn, warn_fn, limbo);
1077 CdlPackage package = dynamic_cast<CdlPackage>(this->lookup(*name_i));
1078 CYG_LOOP_INVARIANT_CLASSC(package);
1079 package->loaded_for_hardware = true;
1082 CdlParse::report_warning(interp, "",
1083 std::string("The target specification lists a package `") + *name_i +
1084 "' which is not present in the component repository.");
1088 current_hardware = target_name;
1091 // Cancel all operations up to and including the rename_op
1092 CdlTransactionCommitCancelOp* cancel_op = 0;
1094 cancel_op = transaction->get_last_commit_cancel_op();
1095 CYG_LOOP_INVARIANTC(0 != cancel_op);
1096 transaction->cancel_last_commit_cancel_op();
1097 } while(cancel_op != rename_op);
1101 // There may have been enables/disables and value data for that target
1102 // FIXME: any problems get ignored quietly. There should at least
1103 // be some warnings.
1104 if ("" != target_name) {
1105 const std::vector<std::string>& enables = database->get_target_enables(target_name);
1106 const std::vector<std::string>& disables = database->get_target_disables(target_name);
1107 const std::vector<std::pair<std::string, std::string> >& set_values = database->get_target_set_values(target_name);
1109 if ((0 != enables.size()) || (0 != disables.size()) || (0 != set_values.size())) {
1110 std::vector<std::string>::const_iterator opt_i;
1112 CdlValuable valuable;
1113 CdlValueFlavor flavor;
1115 for (opt_i = enables.begin(); opt_i != enables.end(); opt_i++) {
1117 node = this->lookup(*opt_i);
1119 valuable = dynamic_cast<CdlValuable>(node);
1120 if (0 != valuable) {
1123 if (0 != valuable) {
1124 flavor = valuable->get_flavor();
1125 if ((CdlValueFlavor_Bool == flavor) || (CdlValueFlavor_BoolData == flavor)) {
1126 valuable->enable(transaction, CdlValueSource_User);
1128 CdlParse::report_warning(interp, std::string("target `") + target_name + "'",
1129 std::string("The option `") + *opt_i +
1130 "' is supposed to be enabled for this target.\n" +
1131 "However the option does not have a bool or booldata flavors.");
1134 CdlParse::report_warning(interp, std::string("target `") + target_name + "'",
1135 std::string("The option `") + *opt_i +
1136 "' is supposed to be enabled for this target.\n" +
1137 "However this option is not in the current configuration.");
1140 for (opt_i = disables.begin(); opt_i != disables.end(); opt_i++) {
1142 node = this->lookup(*opt_i);
1144 valuable = dynamic_cast<CdlValuable>(node);
1146 if (0 != valuable) {
1147 flavor = valuable->get_flavor();
1148 if ((CdlValueFlavor_Bool == flavor) || (CdlValueFlavor_BoolData == flavor)) {
1149 valuable->disable(transaction, CdlValueSource_User);
1151 CdlParse::report_warning(interp, std::string("target `") + target_name + "'",
1152 std::string("The option `") + *opt_i +
1153 "' is supposed to be disabled for this target.\n" +
1154 "However the option does not have a bool or booldata flavors.");
1157 CdlParse::report_warning(interp, std::string("target `") + target_name + "'",
1158 std::string("The option `") + *opt_i +
1159 "' is supposed to be disabled for this target.\n" +
1160 "However this option is not in the current configuration.");
1163 std::vector<std::pair<std::string,std::string> >::const_iterator value_i;
1164 for (value_i = set_values.begin(); value_i != set_values.end(); value_i++) {
1166 node = this->lookup(value_i->first);
1168 valuable = dynamic_cast<CdlValuable>(node);
1170 if (0 != valuable) {
1171 flavor = valuable->get_flavor();
1172 if ((CdlValueFlavor_BoolData == flavor) || (CdlValueFlavor_Data == flavor)) {
1173 valuable->set_value(transaction, value_i->second, CdlValueSource_User);
1175 CdlParse::report_warning(interp, std::string("target `") + target_name + "'",
1176 std::string("The option `") + *opt_i +
1177 "' is supposed to be given the value `" + value_i->second +
1178 "' for this target.\n" +
1179 "However the option does not have a data or booldata flavor.");
1182 CdlParse::report_warning(interp, std::string("target `") + target_name + "'",
1183 std::string("The option `") + *opt_i +
1184 "' is supposed to be given the value `" + value_i->second +
1185 "' for this target.\n" +
1186 "However this option is not in the current configuration.");
1192 CYG_REPORT_RETURN();
1196 CdlConfigurationBody::unload_hardware(CdlTransaction transaction, bool limbo)
1198 CYG_REPORT_FUNCNAME("CdlConfiguration::unload_hardware");
1199 CYG_REPORT_FUNCARG3XV(this, transaction, limbo);
1200 CYG_PRECONDITION_THISC();
1202 CdlConfiguration_CommitCancelHardwareName* rename_op = new CdlConfiguration_CommitCancelHardwareName(current_hardware);
1204 transaction->add_commit_cancel_op(rename_op);
1209 current_hardware = "";
1212 const std::vector<CdlLoadable>& loadables = this->get_loadables();
1213 for (int i = (int) loadables.size() - 1; i >= 0; i--) {
1214 CdlPackage package = dynamic_cast<CdlPackage>(loadables[i]);
1215 if ((0 != package) && package->belongs_to_hardware()) {
1216 this->unload_package(transaction, package, limbo);
1220 CdlTransactionCommitCancelOp* cancel_op = 0;
1222 cancel_op = transaction->get_last_commit_cancel_op();
1223 CYG_LOOP_INVARIANTC(0 != cancel_op);
1224 transaction->cancel_last_commit_cancel_op();
1225 } while(cancel_op != rename_op);
1229 CYG_REPORT_RETURN();
1233 //{{{ CdlConfiguration::set_template() etc
1235 // ----------------------------------------------------------------------------
1237 CdlConfigurationBody::set_template_file(CdlTransaction transaction, std::string filename,
1238 CdlDiagnosticFnPtr error_fn, CdlDiagnosticFnPtr warn_fn, bool limbo)
1240 CYG_REPORT_FUNCNAME("CdlConfiguration::set_template_file");
1241 CYG_REPORT_FUNCARG3XV(this, transaction, limbo);
1242 CYG_PRECONDITION_THISC();
1245 CdlConfiguration_CommitCancelTemplateName* rename_op = new CdlConfiguration_CommitCancelTemplateName(current_template);
1247 // The hard work is done by add(), which loads in a partial savefile.
1248 // This can have undesirable side effects: changing the name,
1249 // description, or hardware settings. It must be possible to undo
1251 std::string saved_name = this->get_name();
1252 std::string saved_description = this->get_description();
1253 std::string saved_hardware = this->get_hardware();
1255 // New packages will end up at the end of the loadables vector.
1256 // Each new package needs to be registered as a template one.
1257 // NOTE: this may break if we start doing more interesting things
1259 const std::vector<CdlLoadable>& loadables = this->get_loadables();
1260 unsigned int load_i = loadables.size();
1263 transaction->add_commit_cancel_op(rename_op);
1264 const std::vector<CdlLoadable>& loadables = this->get_loadables();
1265 for (i = (int) loadables.size() - 1; i >= 0; i--) {
1266 CdlPackage package = dynamic_cast<CdlPackage>(loadables[i]);
1267 if ((0 != package) && package->belongs_to_template()) {
1268 this->unload_package(transaction, package, limbo);
1271 current_template = "";
1273 this->add(transaction, filename, error_fn, warn_fn);
1274 this->current_template = filename;
1275 this->set_name(saved_name);
1276 this->description = saved_description;
1277 this->current_hardware = saved_hardware;
1279 for ( ; load_i < loadables.size(); load_i++) {
1280 CdlPackage pkg = dynamic_cast<CdlPackage>(loadables[load_i]);
1281 CYG_ASSERT_CLASSC(pkg);
1282 pkg->loaded_for_template = true;
1287 this->set_name(saved_name);
1288 this->description = saved_description;
1289 this->current_hardware = saved_hardware;
1291 // Cancel all operations up to and including the rename_op
1292 CdlTransactionCommitCancelOp* cancel_op = 0;
1294 cancel_op = transaction->get_last_commit_cancel_op();
1295 CYG_LOOP_INVARIANTC(0 != cancel_op);
1296 transaction->cancel_last_commit_cancel_op();
1297 } while(cancel_op != rename_op);
1302 CYG_REPORT_RETURN();
1306 CdlConfigurationBody::unload_template(CdlTransaction transaction, bool limbo)
1308 CYG_REPORT_FUNCNAME("CdlConfiguration::unload_template");
1309 CYG_REPORT_FUNCARG2XV(this, transaction);
1310 CYG_PRECONDITION_THISC();
1311 CYG_PRECONDITION_CLASSC(transaction);
1313 CdlConfiguration_CommitCancelTemplateName* rename_op = new CdlConfiguration_CommitCancelTemplateName(current_template);
1315 transaction->add_commit_cancel_op(rename_op);
1320 current_template = "";
1323 const std::vector<CdlLoadable>& loadables = this->get_loadables();
1324 for (int i = (int) loadables.size() - 1; i >= 0; i--) {
1325 CdlPackage package = dynamic_cast<CdlPackage>(loadables[i]);
1326 if ((0 != package) && package->belongs_to_template()) {
1327 this->unload_package(transaction, package, limbo);
1331 CdlTransactionCommitCancelOp* cancel_op = 0;
1333 cancel_op = transaction->get_last_commit_cancel_op();
1334 CYG_LOOP_INVARIANTC(0 != cancel_op);
1335 transaction->cancel_last_commit_cancel_op();
1336 } while(cancel_op != rename_op);
1340 CYG_REPORT_RETURN();
1344 //{{{ Persistence support
1346 //{{{ initialize_savefile_support()
1348 // ----------------------------------------------------------------------------
1349 // Initialization. The purpose of this code is to determine all the
1350 // commands that can end up in a particular savefile. This includes
1351 // the cdl_configuration command, commands relevant to packages,
1352 // options, and components, the generic library commands, and
1353 // application-specific commands.
1355 // This is a virtual function, it may get invoked indirectly from
1356 // e.g. CdlToplevel::add_savefile_command().
1359 CdlConfigurationBody::initialize_savefile_support()
1361 CYG_REPORT_FUNCNAME("CdlConfiguration::initialize_savefile_support");
1362 CYG_REPORT_FUNCARG1XV(this);
1363 CYG_PRECONDITION_THISC();
1365 // Start with the generic stuff such as cdl_savefile_version and
1367 this->CdlToplevelBody::initialize_savefile_support();
1369 // Now add in the cdl_configuration command and its subcommands.
1370 this->add_savefile_command("cdl_configuration", 0, &savefile_configuration_command);
1371 this->add_savefile_subcommand("cdl_configuration", "description", 0, &savefile_description_command);
1372 this->add_savefile_subcommand("cdl_configuration", "hardware", 0, &savefile_hardware_command);
1373 this->add_savefile_subcommand("cdl_configuration", "template", 0, &savefile_template_command);
1374 this->add_savefile_subcommand("cdl_configuration", "package", 0, &savefile_package_command);
1376 CdlPackageBody::initialize_savefile_support(this);
1377 CdlComponentBody::initialize_savefile_support(this);
1378 CdlOptionBody::initialize_savefile_support(this);
1379 CdlInterfaceBody::initialize_savefile_support(this);
1383 //{{{ CdlConfiguration::save() - internal
1385 // ----------------------------------------------------------------------------
1386 // The exported interface is CdlConfiguration::save(). This takes a single
1387 // argument, a filename. It opens the file, and then invokes various
1388 // functions that output the relevants bits of the file.
1390 // This member function is responsible for outputting a cdl_configuration
1394 CdlConfigurationBody::save(CdlInterpreter interp, Tcl_Channel chan, int indentation, bool minimal)
1396 CYG_REPORT_FUNCNAME("CdlConfiguration::save");
1397 CYG_REPORT_FUNCARG5XV(this, interp, chan, indentation, minimal);
1398 CYG_PRECONDITION_THISC();
1399 CYG_PRECONDITION_CLASSC(interp);
1400 CYG_PRECONDITIONC(0 == indentation);
1402 std::string text = "";
1405 "# This section defines the toplevel configuration object. The only\n\
1406 # values that can be changed are the name of the configuration and\n\
1407 # the description field. It is not possible to modify the target,\n\
1408 # the template or the set of packages simply by editing the lines\n\
1409 # below because these changes have wide-ranging effects. Instead\n\
1410 # the appropriate tools should be used to make such modifications.\n\
1414 text += "cdl_configuration " + CdlInterpreterBody::quote(this->get_name()) + " {\n";
1416 std::string config_data = this->get_description();
1417 if (!minimal || ("" != text)) {
1418 text += " description " + CdlInterpreterBody::quote(config_data) + " ;\n";
1421 // Repeat the warning.
1423 text += "\n # These fields should not be modified.\n";
1425 config_data = this->get_hardware();
1426 if ("" != config_data) {
1427 text += " hardware " + CdlInterpreterBody::quote(config_data) + " ;\n";
1429 config_data = this->get_template();
1430 if ("" != config_data) {
1431 text += " template " + CdlInterpreterBody::quote(config_data) + " ;\n";
1433 std::vector<CdlLoadable>::const_iterator load_i;
1434 const std::vector<CdlLoadable>& packages = get_loadables();
1435 for (load_i = packages.begin(); load_i != packages.end(); load_i++) {
1436 CdlPackage pkg = dynamic_cast<CdlPackage>(*load_i);
1437 CYG_ASSERT_CLASSC(pkg);
1438 text += " package ";
1439 if (pkg->belongs_to_template()) {
1440 text += "-template ";
1442 if (pkg->belongs_to_hardware()) {
1443 text += "-hardware ";
1445 text += CdlInterpreterBody::quote(pkg->get_name()) + " " + CdlInterpreterBody::quote(pkg->get_value()) + " ;\n";
1448 interp->write_data(chan, text);
1450 // If the package was loaded from a file then there may be additional
1451 // data associated with the configuration that is not currently
1452 // recognised. This call preserves that data.
1453 this->CdlNodeBody::save(interp, chan, indentation + 4, minimal);
1455 interp->write_data(chan, "};\n\n");
1457 CYG_REPORT_RETURN();
1461 //{{{ CdlConfiguration::save() - exported interface
1463 // ----------------------------------------------------------------------------
1464 // This is the exported interface for saving a configuration. The specified
1465 // file is opened via the appropriate Tcl library routines, and then the
1466 // relevant member functions are invoked to output the actual configuration
1470 CdlConfigurationBody::save(std::string filename, bool minimal)
1472 CYG_REPORT_FUNCNAME("CdlConfiguration::save");
1473 CYG_REPORT_FUNCARG2XV(this, minimal);
1474 CYG_PRECONDITION_THISC();
1475 CYG_PRECONDITIONC("" != filename);
1477 // Make sure that the savefile support is properly initialized.
1478 // This happens during the first save or load operation, or when
1479 // the application starts to register its own savefile extensions.
1480 if (!CdlToplevelBody::savefile_support_initialized()) {
1481 this->initialize_savefile_support();
1484 // A Tcl interpreter is needed for the call to OpenFileChannel(),
1485 // and will also be passed to the individual save functions.
1486 CdlInterpreter interp = this->get_interpreter();
1487 CYG_ASSERT_CLASSC(interp);
1489 // Do not worry about forward vs. backward slashes, since the filename
1490 // is not manipulated in any way. Instead just pass it to Tcl.
1491 Tcl_Channel chan = Tcl_OpenFileChannel(interp->get_tcl_interpreter(), const_cast<char*>(filename.c_str()), "w", 0666);
1493 throw CdlInputOutputException("Unable to open file " + filename + "\n" + interp->get_result());
1496 // The channel may end up being registered in various different
1497 // interpreters, so Tcl_Close() is not the right way to close down
1498 // the channel. Instead Tcl_RegisterChannel() should be used here
1499 // to provide reference counting semantics.
1500 Tcl_RegisterChannel(0, chan);
1502 // A try/catch body is needed here to make sure that the file gets
1503 // properly cleaned up.
1508 interp->write_data(chan, "# eCos saved configuration\n\n");
1511 CdlToplevelBody::save_separator(interp, chan, "commands", minimal);
1512 this->CdlToplevelBody::save_command_details(interp, chan, 0, minimal);
1513 CdlToplevelBody::save_separator(interp, chan, "toplevel", minimal);
1514 this->save(interp, chan, 0, minimal);
1515 CdlToplevelBody::save_separator(interp, chan, "conflicts", minimal);
1516 this->CdlToplevelBody::save_conflicts(interp, chan, 0, minimal);
1517 CdlToplevelBody::save_separator(interp, chan, "contents", minimal);
1518 this->CdlContainerBody::save(interp, chan, 0, minimal);
1519 this->save_unsupported_commands(interp, chan, 0, minimal);
1522 Tcl_UnregisterChannel(0, chan);
1523 // NOTE: deleting the file is necessary, it is a bad idea to
1524 // end up with incomplete save files. It would be even better
1525 // to write to a temporary file and only overwrite the old
1526 // savefile on success.
1528 // Tcl does not provide direct access to the file delete
1529 // facility, so it is necessary to evaluate a script. This
1530 // introduces quoting and security problems, since the
1531 // filename might contain spaces, square brackets, braces...
1532 // To avoid these problems a variable is used.
1533 interp->set_variable("__cdlconfig_filename", filename);
1534 interp->eval("file delete $__cdlconfig_filename", tmp);
1535 interp->unset_variable("__cdlconfig_filename");
1540 // This call will perform the appropriate close.
1541 Tcl_UnregisterChannel(0, chan);
1545 //{{{ CdlConfiguration::load() and add()
1547 // ----------------------------------------------------------------------------
1548 // Most of the work is done in add(). load() simply creates a new configuration
1549 // and then invokes add().
1551 CdlConfigurationBody::load(std::string filename, CdlPackagesDatabase db, CdlInterpreter interp,
1552 CdlDiagnosticFnPtr error_fn, CdlDiagnosticFnPtr warn_fn)
1554 CYG_REPORT_FUNCNAMETYPE("CdlConfiguration::load", "result %p");
1555 CYG_REPORT_FUNCARG4XV(db, interp, error_fn, warn_fn);
1556 CYG_PRECONDITION_CLASSC(db);
1557 CYG_PRECONDITION_CLASSC(interp);
1559 CdlConfiguration result = CdlConfigurationBody::make("eCos", db, interp);
1561 CYG_REPORT_RETVAL(result);
1566 result->add(filename, error_fn, warn_fn);
1567 result->save_file = filename;
1573 CYG_REPORT_RETVAL(result);
1577 // ----------------------------------------------------------------------------
1579 CdlConfigurationBody::add(CdlTransaction transaction, std::string filename,
1580 CdlDiagnosticFnPtr error_fn, CdlDiagnosticFnPtr warn_fn)
1582 CYG_REPORT_FUNCNAME("CdlConfiguration::add");
1583 CYG_REPORT_FUNCARG3XV(this, error_fn, warn_fn);
1584 CYG_PRECONDITION_THISC();
1586 // Initialize the savefile support, so that it is known what
1587 // commands can occur in a savefile.
1588 if (!CdlToplevelBody::savefile_support_initialized()) {
1589 this->initialize_savefile_support();
1592 // The interpreter should not have any left-over junk.
1593 CdlInterpreter interp = this->get_interpreter();
1594 CYG_PRECONDITION_CLASSC(interp);
1595 CYG_ASSERTC(0 == interp->get_loadable());
1596 CYG_ASSERTC(0 == interp->get_container());
1597 CYG_ASSERTC(0 == interp->get_node());
1598 CYG_ASSERTC(0 == interp->get_transaction());
1600 // Keep track of enough information to undo all the changes.
1601 CdlParse::clear_error_count(interp);
1602 CdlInterpreterBody::DiagSupport diag_support(interp, error_fn, warn_fn);
1603 CdlInterpreterBody::ContextSupport context_support(interp, filename);
1606 interp->set_transaction(transaction);
1608 std::vector<CdlInterpreterCommandEntry> commands;
1609 this->get_savefile_commands(commands);
1610 CdlInterpreterBody::CommandSupport interp_cmds(interp, commands);
1612 interp->eval_file(filename);
1614 // All the data has been read in without generating an
1615 // exception. However there may have been errors reported via
1616 // the error_fn handling, and any errors at all should result
1618 int error_count = CdlParse::get_error_count(interp);
1619 if (error_count > 0) {
1621 Cdl::integer_to_string(error_count, tmp);
1622 throw CdlInputOutputException("Invalid savefile \"" + filename + "\".\n" +
1623 tmp + " error" + ((error_count > 1) ? "s" : "") +
1624 " occurred while reading in the savefile data.");
1628 interp->set_transaction(0);
1632 interp->set_transaction(0);
1634 CYG_REPORT_RETURN();
1638 //{{{ savefile commands
1640 // ----------------------------------------------------------------------------
1641 // A cdl_configuration command does not actually do very much. It acts as
1642 // a container for subcommands, and it can be used to change the name.
1644 // The command could also check that the current configuration is empty.
1645 // This is not done, to allow multiple savefiles to be loaded into
1646 // a single configuration in future.
1648 CdlConfigurationBody::savefile_configuration_command(CdlInterpreter interp, int argc, const char *argv[])
1650 CYG_REPORT_FUNCNAMETYPE("CdlConfiguration::savefile_configuration_command", "result %d");
1651 CYG_PRECONDITION_CLASSC(interp);
1653 int result = TCL_OK;
1654 CdlToplevel toplevel = interp->get_toplevel();
1655 CYG_ASSERT_CLASSC(toplevel);
1656 CdlConfiguration config = dynamic_cast<CdlConfiguration>(toplevel);
1657 CYG_ASSERT_CLASSC(config);
1659 std::vector<CdlInterpreterCommandEntry> subcommands;
1660 std::vector<CdlInterpreterCommandEntry>* toplevel_commands = 0;
1663 std::vector<std::pair<std::string,std::string> > options;
1664 int data_index = CdlParse::parse_options(interp, "cdl_configuration command", 0, argc, argv, 1, options);
1666 // A broken cdl_configuration command is pretty fatal, chances are
1667 // that the entire load is going to fail.
1668 if (data_index != (argc - 2)) {
1669 CdlParse::report_error(interp, "", "Invalid cdl_configuration command in savefile, expecting two arguments.");
1671 config->set_name(argv[1]);
1672 config->get_savefile_subcommands("cdl_configuration", subcommands);
1673 toplevel_commands = interp->push_commands(subcommands);
1675 std::string tcl_result;
1676 result = interp->eval(argv[2], tcl_result);
1678 interp->pop_commands(toplevel_commands);
1679 toplevel_commands = 0;
1683 if (0 != toplevel_commands) {
1684 interp->pop_commands(toplevel_commands);
1689 CYG_REPORT_RETVAL(result);
1693 // ----------------------------------------------------------------------------
1695 CdlConfigurationBody::savefile_description_command(CdlInterpreter interp, int argc, const char *argv[])
1697 CYG_REPORT_FUNCNAME("CdlConfiguration::savefile_description_command");
1698 CYG_PRECONDITION_CLASSC(interp);
1700 CdlToplevel toplevel = interp->get_toplevel();
1701 CYG_ASSERT_CLASSC(toplevel);
1702 CdlConfiguration config = dynamic_cast<CdlConfiguration>(toplevel);
1703 CYG_ASSERT_CLASSC(config);
1705 std::vector<std::pair<std::string,std::string> > options;
1706 int data_index = CdlParse::parse_options(interp, "cdl_configuration/description command", 0, argc, argv, 1, options);
1708 if (data_index != (argc - 1)) {
1709 CdlParse::report_warning(interp, "",
1710 "Ignoring invalid configuration description command, expecting a single argument.");
1712 config->description = argv[1];
1717 // ----------------------------------------------------------------------------
1719 CdlConfigurationBody::savefile_hardware_command(CdlInterpreter interp, int argc, const char *argv[])
1721 CYG_REPORT_FUNCNAME("CdlConfiguration::savefile_hardware_command");
1722 CYG_PRECONDITION_CLASSC(interp);
1724 CdlToplevel toplevel = interp->get_toplevel();
1725 CYG_ASSERT_CLASSC(toplevel);
1726 CdlConfiguration config = dynamic_cast<CdlConfiguration>(toplevel);
1727 CYG_ASSERT_CLASSC(config);
1729 std::vector<std::pair<std::string,std::string> > options;
1730 int data_index = CdlParse::parse_options(interp, "cdl_configuration/hardware command", 0, argc, argv, 1, options);
1732 if (data_index != (argc - 1)) {
1733 CdlParse::report_warning(interp, "", "Ignoring invalid configuration hardware command, expecting a single argument.");
1735 config->current_hardware = argv[1];
1741 // ----------------------------------------------------------------------------
1743 CdlConfigurationBody::savefile_template_command(CdlInterpreter interp, int argc, const char *argv[])
1745 CYG_REPORT_FUNCNAME("CdlConfiguration::savefile_template_command");
1746 CYG_PRECONDITION_CLASSC(interp);
1748 CdlToplevel toplevel = interp->get_toplevel();
1749 CYG_ASSERT_CLASSC(toplevel);
1750 CdlConfiguration config = dynamic_cast<CdlConfiguration>(toplevel);
1751 CYG_ASSERT_CLASSC(config);
1753 std::vector<std::pair<std::string,std::string> > options;
1754 int data_index = CdlParse::parse_options(interp, "cdl_configuration/template command", 0, argc, argv, 1, options);
1756 if (data_index != (argc - 1)) {
1757 CdlParse::report_warning(interp, "", "Ignoring invalid configuration template command, expecting a single argument.");
1759 config->current_template = argv[1];
1765 // ----------------------------------------------------------------------------
1767 CdlConfigurationBody::savefile_package_command(CdlInterpreter interp, int argc, const char *argv[])
1769 CYG_REPORT_FUNCNAME("CdlConfiguration::savefile_package_command");
1770 CYG_PRECONDITION_CLASSC(interp);
1772 CdlToplevel toplevel = interp->get_toplevel();
1773 CYG_ASSERT_CLASSC(toplevel);
1774 CdlConfiguration config = dynamic_cast<CdlConfiguration>(toplevel);
1775 CYG_ASSERT_CLASSC(config);
1776 CdlPackagesDatabase db = config->get_database();
1777 CYG_ASSERT_CLASSC(db);
1779 std::string pkgname;
1780 std::string pkgversion;
1783 std::vector<std::pair<std::string,std::string> > options;
1784 static const char *optlist[] = {
1789 int data_index = CdlParse::parse_options(interp, "cdl_configuration/package command", optlist, argc, argv, 1, options);
1791 if (data_index == (argc - 1)) {
1792 CdlParse::report_warning(interp, "", std::string("Missing version information for package `")
1793 + argv[argc - 1] + "'.");
1794 pkgname = argv[argc - 1];
1796 } else if (data_index == (argc - 2)) {
1797 pkgname = argv[argc - 2];
1798 pkgversion = argv[argc - 1];
1800 // If we cannot load all the packages then much of the
1801 // savefile is likely to be problematical.
1802 CdlParse::report_error(interp, "", "Invalid cdl_configuration/package command, expecting name and version");
1803 CYG_REPORT_RETURN();
1807 if (0 != config->lookup(pkgname)) {
1808 // If the package was already loaded, check the version string. If the versions
1809 // are identical then we do not need to worry any further. Otherwise a mismatch
1810 // warning is appropriate.
1811 CdlNode node = config->lookup(pkgname);
1812 CYG_ASSERT_CLASSC(node);
1813 pkg = dynamic_cast<CdlPackage>(node);
1815 // The name is in use, but it is not a package
1816 CdlParse::report_error(interp, "",
1817 std::string("Unable to load package `") + pkgname + "', the name is already in use.");
1818 } else if (pkgversion != pkg->get_value()) {
1819 CdlParse::report_warning(interp, "",
1820 std::string("Cannot load version `") + pkgversion + "' of package `" +
1821 pkgname + "', version `" + pkg->get_value() + "' is already loaded.");
1823 } else if (!db->is_known_package(pkgname)) {
1824 CdlParse::report_error(interp, "",
1825 std::string("Attempt to load an unknown package `") + pkgname + "'.");
1827 if ("" != pkgversion) {
1828 const std::vector<std::string>& versions = db->get_package_versions(pkgname);
1829 if (versions.end() == std::find(versions.begin(), versions.end(), pkgversion)) {
1830 CdlParse::report_warning(interp, "",
1831 std::string("The savefile specifies version `") + pkgversion +
1832 "' for package `" + pkgname + "'\nThis version is not available.\n" +
1833 "Using the most recent version instead.");
1837 CdlDiagnosticFnPtr error_fn = interp->get_error_fn_ptr();
1838 CdlDiagnosticFnPtr warn_fn = interp->get_warning_fn_ptr();
1839 CdlTransaction transaction = interp->get_transaction();
1840 config->load_package(transaction, pkgname, pkgversion, error_fn, warn_fn, false);
1841 CdlNode pkg_node = config->lookup(pkgname);
1842 CYG_ASSERTC(0 != pkg_node);
1843 pkg = dynamic_cast<CdlPackage>(pkg_node);
1844 CYG_ASSERT_CLASSC(pkg);
1847 if ((0 != pkg) && (0 != options.size())) {
1848 std::vector<std::pair<std::string,std::string> >::const_iterator opt_i;
1849 for (opt_i = options.begin(); opt_i != options.end(); opt_i++) {
1850 if (opt_i->first == "template") {
1851 pkg->loaded_for_template = true;
1852 } else if (opt_i->first == "hardware") {
1853 pkg->loaded_for_hardware = true;