📄 config.cxx
字号:
//// 3) create the package object, and add it to the toplevel of the current// configuration. It may get reparented later on. Part of the creation// process is to allocate a new slave interpreter, which can be updated// with various bits of information.//// 4) evaluate the toplevel script. Subsidiary component scripts will// get evaluated as a side effect. The various nodes will be added// to the hierarchy as they are created, but no property binding// happens yet.//// Any failure up to this point should result in the entire package// being removed from the hierarchy and then destroyed, thus leaving// the configuration in its original state.//// 5) now property binding needs to take place. This can have lots// of side effects, e.g. default values may get calculated, the// hierarchy may change because of parent properties, etc.// The work is done inside CdlLoadable::bind() which will undo// everything on failure - although bad_alloc is the only// failure that should occur.//// 6) load operations can get cancelled, so a suitable commit/cancel// operation needs to allocated and added to the transaction.//// 7) if limbo is enabled, previous values should be extracted from// limbo if at all possible. In addition the package's value can// be set to its version.voidCdlConfigurationBody::load_package(CdlTransaction transaction, std::string name, std::string version, CdlDiagnosticFnPtr error_fn, CdlDiagnosticFnPtr warn_fn, bool limbo) throw(CdlInputOutputException, CdlParseException, std::bad_alloc){ CYG_REPORT_FUNCNAME("CdlConfiguration::load_package"); CYG_REPORT_FUNCARG1XV(this); CYG_PRECONDITION_THISC(); CYG_PRECONDITION_CLASSC(transaction); CYG_PRECONDITIONC("" != name); // Locate the database entry. Also check the version (filling it in if necessary). // Get hold of the package directory and the initial script. if (!database->is_known_package(name)) { throw CdlInputOutputException("Unknown package " + name); } const std::vector<std::string>& versions = database->get_package_versions(name); if ("" == version) { version = *(versions.begin()); } else { if (std::find(versions.begin(), versions.end(), version) == versions.end()) { throw CdlInputOutputException("Package " + name + " does not have an installed version " + version); } } std::string directory = database->get_package_directory(name); std::string script = database->get_package_script(name); CYG_ASSERTC(("" != directory) && ("" != script)); // Check that the directory actually exists. For this the configuration's own // interpreter can be used. CdlInterpreter interp = get_interpreter(); CYG_ASSERT_CLASSC(interp); std::string tcl_cmd = "regsub -all -- {\\\\} [file join " + directory + " " + version + "] / result; return $result"; std::string tcl_result; if (TCL_OK != interp->eval(tcl_cmd, tcl_result)) { throw CdlInputOutputException("Cannot load package " + name + ", internal error constructing pathname"); } directory = tcl_result; tcl_cmd = "file isdirectory [file join \"" + database->get_component_repository() + "\" " + directory + "]"; if ((TCL_OK != interp->eval(tcl_cmd, tcl_result)) || ("1" != tcl_result)) { throw CdlInputOutputException("Cannot load package " + name + ", there is no directory " + directory); } // Make sure that there is no name conflict. No resources have been allocated // yet, so this is a good time. CdlNode node = lookup(name); if (0 != node) { if (0 != dynamic_cast<CdlPackage>(node)) { throw CdlInputOutputException("Package " + name + " is already loaded"); } else { std::string msg = "Name clash for package " + name + ",there is a " + node->get_class_name() + " " + name + " already loaded"; CdlLoadable owner_pkg = node->get_owner(); if (0 != owner_pkg) { msg += " in package " + owner_pkg->get_name(); } throw CdlInputOutputException(msg); } } // Now create the package object itself. CdlPackage package = 0; bool bound = false; CdlConfiguration_CommitCancelLoad* load_op = 0; try { package = new CdlPackageBody(name, this, directory); // The package should be added to the hierarchy immediately. // All nodes will get added to the hierarchy as they are // created, an operation that has to be undone during // failure. this->add_node(package, this, package); // Load the package data. The various nodes will all end up // in a hierarchy below the package, but without any checks // for name conflicts etc and ignoring any re-parenting. CdlInterpreter interp = package->get_interpreter(); CYG_ASSERT_CLASSC(interp); interp->add_command("unknown", &CdlParse::unknown_command); // Next figure out the script name, and make sure that it exists. std::string actual_script = package->find_absolute_file(script, "cdl"); if ("" == actual_script) { throw CdlInputOutputException("Package " + name + ", unable to find initial script " + script); } tcl_cmd = "file isfile \"" + actual_script + "\""; if ((TCL_OK != interp->eval(tcl_cmd, tcl_result)) || ("1" != tcl_result)) { throw CdlInputOutputException("Package " + name + ", " + actual_script + " is not a CDL script"); } // The script is valid. Set up the interpreter appropriately. CdlContainer old_container = interp->push_container(package); std::string old_filename = interp->push_filename(actual_script); CdlDiagnosticFnPtr old_error_fn = interp->push_error_fn_ptr(error_fn); CdlDiagnosticFnPtr old_warn_fn = interp->push_warning_fn_ptr(warn_fn); CdlParse::clear_error_count(interp); static CdlInterpreterCommandEntry commands[] = { CdlInterpreterCommandEntry("cdl_package", &CdlPackageBody::parse_package ), CdlInterpreterCommandEntry("cdl_component", &CdlComponentBody::parse_component ), CdlInterpreterCommandEntry("cdl_option", &CdlOptionBody::parse_option ), CdlInterpreterCommandEntry("cdl_interface", &CdlInterfaceBody::parse_interface ), CdlInterpreterCommandEntry("cdl_dialog", &CdlDialogBody::parse_dialog ), CdlInterpreterCommandEntry("cdl_wizard", &CdlWizardBody::parse_wizard ), CdlInterpreterCommandEntry("", 0 ) }; std::vector<CdlInterpreterCommandEntry> new_commands; for (int i = 0; 0 != commands[i].command; i++) { new_commands.push_back(commands[i]); } std::vector<CdlInterpreterCommandEntry>* old_commands = interp->push_commands(new_commands); // The interpreter is now ready. if (TCL_OK != interp->eval_file(actual_script, tcl_result)) { throw CdlInputOutputException("Package " + name + ", error executing CDL script.\n" + tcl_result); } // Clean out the commands etc. This interpreter may get used again // in future, and it should not be possible to define new options // etc. in that invocation. interp->remove_command("unknown"); interp->pop_commands(old_commands); interp->pop_container(old_container); interp->pop_filename(old_filename); interp->pop_error_fn_ptr(old_error_fn); interp->pop_warning_fn_ptr(old_warn_fn); // All the data has been read in without generating an // exception. However there may have been errors reported via // the parse_error_fn, and any errors at all should result // in an exception. int error_count = CdlParse::get_error_count(interp); if (error_count > 0) { std::string tmp; Cdl::integer_to_string(error_count, tmp); throw CdlParseException("Package " + name + ", " + tmp + " error" + ((error_count > 1) ? "s" : "") + " occurred while reading in the CDL data."); } // All the data has been read in, implying that there are no // fatal problems with the data. Now try to bind all // references to and from this loadable. package->bind(transaction); bound = true; // Finally, create a suitable transaction commit/cancel object // and add it to the transaction. load_op = new CdlConfiguration_CommitCancelLoad(package); transaction->add_commit_cancel_op(load_op); } catch (...) { // Something went wrong during the create or load. It is necessary // to delete the package. Undo all the operations above, in // reverse order. The add_commit_cancel_op() was the last step, // so need not be undone here. if (0 != load_op) { delete load_op; } if (0 != package) { // Note: no attempt is made to recover from errors here if (bound) { package->unbind(transaction); } this->remove_loadable_from_toplevel(package); delete package; } throw; } // FIXME: implement limbo support // We also have a sensible value for the package as a whole. // Use this value for both default and user - after all the // user has selected the package. package->enable_and_set_value(transaction, version, CdlValueSource_Default); package->enable_and_set_value(transaction, version, CdlValueSource_User); CYG_REPORT_RETURN();}//}}}//{{{ CdlConfiguration::unload_package() // ----------------------------------------------------------------------------// Unloading a package is very simple. If requested, save all current// values to limbo: there is no point in saving default values, these// will get recalculated from the default_value property anyway;// inferred values should be saved, there is no guarantee that the exact// same value will be calculated again, and if the inferred value is no// longer correct then the inference engine can freely update it.//// Next, unbind the package and remove it from the hierarchy. These// operations are reversible if the transaction gets cancelled.// A suitable transaction commit/cancel object is created and// added to the transaction.voidCdlConfigurationBody::unload_package(CdlTransaction transaction, CdlPackage package, bool limbo){ CYG_REPORT_FUNCNAME("CdlConfiguration::unload_package"); CYG_REPORT_FUNCARG4XV(this, transaction, package, limbo); CYG_INVARIANT_THISC(CdlConfigurationBody); CYG_INVARIANT_CLASSC(CdlTransactionBody, transaction); CYG_PRECONDITION_CLASSC(package); if (limbo) { const std::vector<CdlNode>& pkg_contents = package->get_owned(); std::vector<CdlNode>::const_iterator node_i; for (node_i = pkg_contents.begin(); node_i != pkg_contents.end(); node_i++) { CdlValuable valuable = dynamic_cast<CdlValuable>(*node_i); if (0 != valuable) { if (valuable->has_source(CdlValueSource_Inferred) || valuable->has_source(CdlValueSource_Wizard) || valuable->has_source(CdlValueSource_User)) { set_limbo_value(valuable); } } } } bool unbound = false; bool removed = false; CdlConfiguration_CommitCancelUnload* unload_op = 0; try { package->unbind(transaction); unbound = true; this->remove_loadable_from_toplevel(package); removed = true; unload_op = new CdlConfiguration_CommitCancelUnload(package); transaction->add_commit_cancel_op(unload_op); } catch(...) { if (0 != unload_op) { delete unload_op; } if (removed) { this->add_loadable_to_toplevel(package); } if (unbound) { package->bind(transaction); } throw; } CYG_REPORT_RETURN();}//}}}//{{{ CdlConfiguration::change_package_version() // ----------------------------------------------------------------------------// Changing a package version is just a case of unloading the old version// and then loading in the new version. Because this all happens in the// context of a transaction it is possible to undo the unload on// failure, and the whole transaction can be cancelled at a higher level.voidCdlConfigurationBody::change_package_version(CdlTransaction transaction, CdlPackage package, std::string new_version, CdlDiagnosticFnPtr error_fn, CdlDiagnosticFnPtr warn_fn, bool limbo) throw(CdlInputOutputException, CdlParseException, std::bad_alloc){ CYG_REPORT_FUNCNAME("CdlConfiguration::change_package_version"); CYG_REPORT_FUNCARG3XV(this, transaction, package); CYG_PRECONDITION_THISC(); CYG_INVARIANT_CLASSC(CdlTransactionBody, transaction); CYG_PRECONDITION_CLASSC(package); // "" is valid for the version, it indicates the default // Since the package is already loaded it must be in the database, // but it is possible that the desired version does not exist. std::string name = package->get_name(); const std::vector<std::string>& pkg_versions = database->get_package_versions(name); if ("" == new_version) { new_version = *(pkg_versions.begin()); } else if (std::find(pkg_versions.begin(), pkg_versions.end(), new_version) == pkg_versions.end()) { throw CdlInputOutputException("Version " + new_version + " of package " + name + " is not installed."); } bool unloaded = false; try { this->unload_package(transaction, package, limbo); unloaded = true; this->load_package(transaction, name, new_version, error_fn, warn_fn, limbo); } catch(...) { if (unloaded) { // There should be a commit/cancel op for the unload package step. // This can be undone. CdlTransactionCommitCancelOp* unload_op = transaction->get_last_commit_cancel_op(); CYG_ASSERTC(0 != unload_op); CYG_ASSERTC(0 != dynamic_cast<CdlConfiguration_CommitCancelUnload*>(unload_op)); transaction->cancel_last_commit_cancel_op(); CYG_UNUSED_PARAM(CdlTransactionCommitCancelOp*, unload_op); } throw; } CYG_REPORT_RETURN();}//}}}//{{{ CdlConfiguration::set_hardware() etc. // ----------------------------------------------------------------------------// Setting the hardware involves unloading the old hardware, if any, and// then loading in the new one. Obviously this should only happen if// the new hardware name is valid. It would be possible to optimise for// the case where the old and new hardware are the same, subject// to dynamic database reload support.voidCdlConfigurationBody::set_hardware(CdlTransaction transaction, std::string target_name,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -