//{{{ Banner //============================================================================ // // database.cxx // // Temporary implementation of the CdlPackagesDatabase class // Implementations of the temporary CdlTargetsDatabase and // CdlTemplatesDatabase classes. // //============================================================================ //####COPYRIGHTBEGIN#### // // ---------------------------------------------------------------------------- // Copyright (C) 2002, 2003 Bart Veer // Copyright (C) 1999, 2000, 2001 Red Hat, Inc. // // This file is part of the eCos host tools. // // This program is free software; you can redistribute it and/or modify it // under the terms of the GNU General Public License as published by the Free // Software Foundation; either version 2 of the License, or (at your option) // any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // // You should have received a copy of the GNU General Public License along with // this program; if not, write to the Free Software Foundation, Inc., // 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // // ---------------------------------------------------------------------------- // //####COPYRIGHTEND#### //============================================================================ //#####DESCRIPTIONBEGIN#### // // Author(s): bartv // Contact(s): bartv // Date: 1999/01/21 // Version: 0.02 // //####DESCRIPTIONEND#### //============================================================================ //}}} //{{{ #include's // ---------------------------------------------------------------------------- #include "cdlconfig.h" // Get the infrastructure types, assertions, tracing and similar // facilities. #include #include // defines everything implemented in this module. // It implicitly supplies , and because // the class definitions rely on these headers. #include // strcmp() is useful when dealing with Tcl strings. #include //}}} //{{{ Statics // ---------------------------------------------------------------------------- // Some test cases may want to read in a file other than // "ecos.db", e.g. to facilitate testing the error conditions. char* CdlPackagesDatabaseBody::database_name = "ecos.db"; // Should warnings be issued for minor database inconsistencies? bool CdlPackagesDatabaseBody::verbose_mode = false; // The new_package etc. commands need to store the name of the // current package so that subsequent commands can do the right thing. // Using constant strings as the key avoids typo problems. const char* dbparser_pkgname = "::dbparser_pkgname"; const char* dbparser_pkgdata = "__cdl_dbparser_pkgdata"; const char* dbparser_targetname = "::dbparser_targetname"; const char* dbparser_targetdata = "__cdl_dbparser_targetdata"; const char* dbparser_component_repository = "::component_repository"; const char* dbparser_database_key = "__dbparser_key"; // for assoc data const char* template_description_key = "__cdl_extract_template_description"; // ditto const char* template_packages_key = "__cdl_extract_template_packages"; // These are useful for generating diagnostics. static std::string diag_package = std::string("package "); static std::string diag_target = std::string("target "); CYGDBG_DEFINE_MEMLEAK_COUNTER(CdlPackagesDatabaseBody); //}}} //{{{ Tcl commands for the parser //{{{ CdlDbParser class // ---------------------------------------------------------------------------- // Commands that get invoked from inside the Tcl interpreter. These // need access to the internals of the database objects, which can be // achieved by making them static members of a CdlDbParser class. class CdlDbParser { public: static int new_package(CdlInterpreter, int, const char*[]); static int package_description(CdlInterpreter, int, const char*[]); static int package_alias(CdlInterpreter, int, const char*[]); static int package_directory(CdlInterpreter, int, const char*[]); static int package_script(CdlInterpreter, int, const char*[]); static int package_hardware(CdlInterpreter, int, const char*[]); static int new_target(CdlInterpreter, int, const char*[]); static int target_description(CdlInterpreter, int, const char*[]); static int target_alias(CdlInterpreter, int, const char*[]); static int target_packages(CdlInterpreter, int, const char*[]); static int target_enable(CdlInterpreter, int, const char*[]); static int target_disable(CdlInterpreter, int, const char*[]); static int target_set_value(CdlInterpreter, int, const char*[]); }; //}}} //{{{ CdlDbParser::package-related // ---------------------------------------------------------------------------- // package int CdlDbParser::new_package(CdlInterpreter interp, int argc, const char* argv[]) { CYG_REPORT_FUNCNAMETYPE("CdlDbParser::new_package", "result %d"); CYG_REPORT_FUNCARG1XV(argc); CYG_PRECONDITION_CLASSC(interp); CdlPackagesDatabase db = static_cast(interp->get_assoc_data(dbparser_database_key)); CYG_INVARIANT_CLASSC(CdlPackagesDatabaseBody, db); if (3 != argc) { if (argc < 2) { CdlParse::report_error(interp, "", "Invalid package command, missing name and contents."); } else if (argc == 2) { CdlParse::report_error(interp, diag_package + argv[1], "Invalid package command, missing body."); } else { CdlParse::report_error(interp, diag_package + argv[1], "Invalid package command, expecting just name and body."); } CYG_REPORT_RETVAL(TCL_OK); return TCL_OK; } std::string pkg_name = argv[1]; // The package data is constructed locally. It only gets added to // the database in the absence of errors. bool package_ok = true; int old_error_count = CdlParse::get_error_count(interp); CdlPackagesDatabaseBody::package_data package; package.description = ""; package.directory = ""; package.script = ""; package.hardware = false; // aliases and versions are vectors and will take care of themselves // And the name had better be valid as well. if (!Cdl::is_valid_cdl_name(pkg_name)) { CdlParse::report_error(interp, diag_package + pkg_name, "This is not a valid CDL name."); } // Sort out the commands, then invoke the script in argv[2]. There is // no need to worry about error recovery here, any errors will be // fatal anyway. CdlInterpreterCommandEntry commands[] = { CdlInterpreterCommandEntry("description", &CdlDbParser::package_description ), CdlInterpreterCommandEntry("alias", &CdlDbParser::package_alias ), CdlInterpreterCommandEntry("directory", &CdlDbParser::package_directory ), CdlInterpreterCommandEntry("script", &CdlDbParser::package_script ), CdlInterpreterCommandEntry("hardware", &CdlDbParser::package_hardware ), CdlInterpreterCommandEntry("", 0 ) }; CdlInterpreterBody::CommandSupport cmds(interp, commands); CdlInterpreterBody::VariableSupport interp_name(interp, dbparser_pkgname, pkg_name); CdlInterpreterBody::AssocSupport interp_data(interp, dbparser_pkgdata, static_cast(&package)); int result = interp->eval(argv[2]); if (TCL_OK == result) { // The body has been parsed OK. Check that it is valid. if ("" == package.directory) { CdlParse::report_error(interp, diag_package + pkg_name, "Missing directory specification."); } if ("" == package.script) { CdlParse::report_error(interp, diag_package + pkg_name, "Missing script specification."); } if (0 == package.aliases.size()) { CdlParse::report_error(interp, diag_package + pkg_name, "At least one alias should be supplied."); } // Additional checks. Is the package directory actually present? // Note that there are scenarios where a package may be listed // in the database but not installed, e.g. an anoncvs checkout // of selected modules. if ("" != package.directory) { std::string repo = interp->get_variable(dbparser_component_repository); CYG_ASSERTC("" != repo); std::string pkgdir = repo + "/" + package.directory; if (!interp->is_directory(pkgdir)) { if (CdlPackagesDatabaseBody::verbose_mode) { CdlParse::report_warning(interp, diag_package + pkg_name, std::string("This package is not present in the component repository.\n" "There is no directory `") + pkgdir + "'."); } package_ok = false; } else { // Now look for version subdirectories. There should be at least one. std::vector subdirs; unsigned int i; interp->locate_subdirs(pkgdir, subdirs); for (i = 0; i < subdirs.size(); i++) { if (("CVS" == subdirs[i]) || ("cvs" == subdirs[i])) { continue; } if ("" != package.script) { if (!(interp->is_file(pkgdir + "/" + subdirs[i] + "/cdl/" + package.script) || interp->is_file(pkgdir + "/" + subdirs[i] + "/" + package.script))) { CdlParse::report_warning(interp, diag_package + pkg_name, std::string("Version subdirectory `") + subdirs[i] + "' does not have a CDL script `" + package.script + "'."); continue; } } package.versions.push_back(subdirs[i]); package.repositories[subdirs[i]] = repo; } if (0 == package.versions.size()) { CdlParse::report_warning(interp, diag_package + pkg_name, "This package does not have any valid version subdirectories."); package_ok = false; } } } } // If the package is still ok, now is the time to add it to the database. // It may be a new package, or there may already be an entry from a previous // repository. if (package_ok && (old_error_count == CdlParse::get_error_count(interp))) { if ( std::find(db->package_names.begin(), db->package_names.end(), pkg_name) == db->package_names.end()) { db->package_names.push_back(pkg_name); db->packages[pkg_name] = package; } else { // Only add versions which are not already present. std::vector::const_iterator version_i; for (version_i = package.versions.begin(); version_i != package.versions.end(); version_i++) { if (std::find(db->packages[pkg_name].versions.begin(), db->packages[pkg_name].versions.end(), *version_i) == db->packages[pkg_name].versions.end()) { db->packages[pkg_name].versions.push_back(*version_i); db->packages[pkg_name].repositories[*version_i] = package.repositories[*version_i]; } } } } CYG_REPORT_RETVAL(result); return result; } // Syntax: description int CdlDbParser::package_description(CdlInterpreter interp, int argc, const char* argv[]) { CYG_REPORT_FUNCNAMETYPE("CdlDbParser::package_description", "result %d"); CYG_REPORT_FUNCARG1XV(argc); CYG_PRECONDITION_CLASSC(interp); std::string name = interp->get_variable(dbparser_pkgname); CYG_ASSERTC("" != name); CdlPackagesDatabaseBody::package_data* package = static_cast(interp->get_assoc_data(dbparser_pkgdata)); if (2 != argc) { CdlParse::report_error(interp, diag_package + name, "Invalid description, expecting a single string."); } else if ("" != package->description) { CdlParse::report_warning(interp, diag_package + name, "A package should have only one description."); } else { package->description = argv[1]; } CYG_REPORT_RETVAL(TCL_OK); return TCL_OK; } // Syntax: alias // For example: alias { "This is an alias" another_alias dummy_name } int CdlDbParser::package_alias(CdlInterpreter interp, int argc, const char* argv[]) { CYG_REPORT_FUNCNAMETYPE("CdlDbParser::package_alias", "result %d"); CYG_REPORT_FUNCARG1XV(argc); CYG_PRECONDITION_CLASSC(interp); std::string name = interp->get_variable(dbparser_pkgname); CYG_ASSERTC("" != name); CdlPackagesDatabaseBody::package_data* package = static_cast(interp->get_assoc_data(dbparser_pkgdata)); // There should be one argument, a list of valid packages. // Also, the alias command should be used only once if (2 != argc) { CdlParse::report_error(interp, diag_package + name, "The alias command should be followed by a list of known aliases."); } else if (0 < package->aliases.size()) { CdlParse::report_warning(interp, diag_package + name, "There should be only one list of aliases."); } else { int list_count = 0; const char** list_entries = 0; Tcl_Interp* tcl_interp = interp->get_tcl_interpreter(); if (TCL_OK != Tcl_SplitList(tcl_interp, CDL_TCL_CONST_CAST(char*, argv[1]), &list_count, CDL_TCL_CONST_CAST(char***, &list_entries))) { CdlParse::report_error(interp, diag_package + name, Tcl_GetStringResult(tcl_interp)); } else { if (0 == list_count) { CdlParse::report_error(interp, diag_package + name, "At least one alias should be supplied."); } else { for (int i = 0; i < list_count; i++) { package->aliases.push_back(list_entries[i]); } } Tcl_Free((char*)list_entries); } } CYG_REPORT_RETVAL(TCL_OK); return TCL_OK; } // Syntax: directory // The path is of course relative to the component repository. int CdlDbParser::package_directory(CdlInterpreter interp, int argc, const char* argv[]) { CYG_REPORT_FUNCNAMETYPE("CdlDbParser::package_directory", "result %d"); CYG_REPORT_FUNCARG1XV(argc); CYG_PRECONDITION_CLASSC(interp); std::string name = interp->get_variable(dbparser_pkgname); CYG_ASSERTC("" != name); CdlPackagesDatabaseBody::package_data* package = static_cast(interp->get_assoc_data(dbparser_pkgdata)); // There should be exactly one argument, and the directory command // should be used only once. if (2 != argc) { CdlParse::report_error(interp, diag_package + name, "Only one directory can be specified."); } else if ("" != package->directory) { CdlParse::report_warning(interp, diag_package + name, "A package can be located in only one directory."); } else { package->directory = argv[1]; } CYG_REPORT_RETVAL(TCL_OK); return TCL_OK; } // Syntax: hardware // There are no arguments. int CdlDbParser::package_hardware(CdlInterpreter interp, int argc, const char* argv[]) { CYG_REPORT_FUNCNAMETYPE("CdlDbParser::package_hardware", "result %d"); CYG_REPORT_FUNCARG1XV(argc); CYG_PRECONDITION_CLASSC(interp); std::string name = interp->get_variable(dbparser_pkgname); CYG_ASSERTC("" != name); CdlPackagesDatabaseBody::package_data* package = static_cast(interp->get_assoc_data(dbparser_pkgdata)); if (1 != argc) { CdlParse::report_error(interp, diag_package + name, "There should be no further data after hardware."); } else if (package->hardware) { CdlParse::report_warning(interp, diag_package + name, "The hardware property should be specified only once"); } else { package->hardware = true; } CYG_REPORT_RETVAL(TCL_OK); return TCL_OK; } // Syntax: script int CdlDbParser::package_script(CdlInterpreter interp, int argc, const char* argv[]) { CYG_REPORT_FUNCNAMETYPE("CdlDbParser::package_script", "result %d"); CYG_REPORT_FUNCARG1XV(argc); CYG_PRECONDITION_CLASSC(interp); std::string name = interp->get_variable(dbparser_pkgname); CYG_ASSERTC("" != name); CdlPackagesDatabaseBody::package_data* package = static_cast(interp->get_assoc_data(dbparser_pkgdata)); // There should be exactly one argument, and the script command // should be used only once if (2 != argc) { CdlParse::report_error(interp, diag_package + name, "Only one CDL script can be specified."); } else if ("" != package->script) { CdlParse::report_warning(interp, diag_package + name, "A package can have only one starting CDL script."); } else { package->script = argv[1]; } CYG_REPORT_RETVAL(TCL_OK); return TCL_OK; } //}}} //{{{ CdlDbParser::target-related // ---------------------------------------------------------------------------- // target int CdlDbParser::new_target(CdlInterpreter interp, int argc, const char* argv[]) { CYG_REPORT_FUNCNAMETYPE("CdlDbParser::new_target", "result %d"); CYG_REPORT_FUNCARG1XV(argc); CYG_PRECONDITION_CLASSC(interp); CdlPackagesDatabase db = static_cast(interp->get_assoc_data(dbparser_database_key)); CYG_INVARIANT_CLASSC(CdlPackagesDatabaseBody, db); if (3 != argc) { if (argc < 2) { CdlParse::report_error(interp, "", "Invalid target command, missing name and contents."); } else if (argc == 2) { CdlParse::report_error(interp, diag_target + argv[1], "Invalid target command, missing body."); } else { CdlParse::report_error(interp, diag_target + argv[1], "Invalid target command, expecting just name and body."); } CYG_REPORT_RETVAL(TCL_OK); return TCL_OK; } std::string target_name = argv[1]; // This may be a duplicate definition if the target was defined in an // earlier repository if (std::find(db->target_names.begin(), db->target_names.end(), target_name) != db->target_names.end()) { CYG_REPORT_RETVAL(TCL_OK); return TCL_OK; } // The target data is constructed locally. It only gets added to the // database in the absence of errors. bool target_ok = true; int old_error_count = CdlParse::get_error_count(interp); CdlPackagesDatabaseBody::target_data target; target.description = ""; // aliases, packages and compiler_flags are vectors and will take care of themselves // Sort out the commands, then invoke the script in argv[2]. There is // no need to worry about error recovery here, any errors will be // fatal anyway. CdlInterpreterCommandEntry commands[] = { CdlInterpreterCommandEntry("description", &CdlDbParser::target_description ), CdlInterpreterCommandEntry("alias", &CdlDbParser::target_alias ), CdlInterpreterCommandEntry("packages", &CdlDbParser::target_packages ), CdlInterpreterCommandEntry("enable", &CdlDbParser::target_enable ), CdlInterpreterCommandEntry("disable", &CdlDbParser::target_disable ), CdlInterpreterCommandEntry("set_value", &CdlDbParser::target_set_value ), CdlInterpreterCommandEntry("", 0 ) }; CdlInterpreterBody::CommandSupport interp_cmds(interp, commands); CdlInterpreterBody::VariableSupport interp_name(interp, dbparser_targetname, target_name); CdlInterpreterBody::AssocSupport interp_data(interp, dbparser_targetdata, static_cast(&target)); int result = interp->eval(argv[2]); if (TCL_OK == result) { if (0 == target.aliases.size()) { CdlParse::report_error(interp, diag_target + target_name, "At least one alias should be supplied."); } // There is no check for > 0 hardware packages. This is an unlikely // scenario but should be allowed for. // Add this target to the list. } if (target_ok && (old_error_count == CdlParse::get_error_count(interp))) { db->target_names.push_back(target_name); db->targets[target_name] = target; } CYG_REPORT_RETVAL(result); return result; } // Syntax: description int CdlDbParser::target_description(CdlInterpreter interp, int argc, const char* argv[]) { CYG_REPORT_FUNCNAMETYPE("CdlDbParser::target_description", "result %d"); CYG_REPORT_FUNCARG1XV(argc); CYG_PRECONDITION_CLASSC(interp); std::string name = interp->get_variable(dbparser_targetname); CYG_ASSERTC("" != name); CdlPackagesDatabaseBody::target_data* target = static_cast(interp->get_assoc_data(dbparser_targetdata)); if (2 != argc) { CdlParse::report_error(interp, diag_target + name, "The target description should be a single string."); } else if ("" != target->description) { CdlParse::report_warning(interp, diag_target + name, "A target should have only one description."); } else { target->description = argv[1]; } CYG_REPORT_RETVAL(TCL_OK); return TCL_OK; } // Syntax: alias // For example: alias { "This is an alias" another_alias dummy_name } int CdlDbParser::target_alias(CdlInterpreter interp, int argc, const char* argv[]) { CYG_REPORT_FUNCNAMETYPE("CdlDbParser::target_alias", "result %d"); CYG_REPORT_FUNCARG1XV(argc); CYG_PRECONDITION_CLASSC(interp); std::string name = interp->get_variable(dbparser_targetname); CYG_ASSERTC("" != name); CdlPackagesDatabaseBody::target_data* target = static_cast(interp->get_assoc_data(dbparser_targetdata)); // There should be one argument, a list of valid aliases // The alias command should be used only once if (2 != argc) { CdlParse::report_error(interp, diag_target + name, "The alias command should be followed by a list of known aliases"); } else if (0 < target->aliases.size()) { CdlParse::report_warning(interp, diag_target + name, "There should be only one list of aliases."); } else { int list_count = 0; const char** list_entries = 0; Tcl_Interp* tcl_interp = interp->get_tcl_interpreter(); if (TCL_OK != Tcl_SplitList(tcl_interp, CDL_TCL_CONST_CAST(char*, argv[1]), &list_count, CDL_TCL_CONST_CAST(char***, &list_entries))) { CdlParse::report_error(interp, diag_target + name, Tcl_GetStringResult(tcl_interp)); } else { if (0 == list_count) { CdlParse::report_error(interp, diag_target + name, "At least one alias should be supplied."); } else { for (int i = 0; i < list_count; i++) { target->aliases.push_back(list_entries[i]); } } Tcl_Free((char*)list_entries); } } CYG_REPORT_RETVAL(TCL_OK); return TCL_OK; } // Syntax: packages ... // For example: packages { CYGPKG_HAL_XXX CYGPKG_HAL_YYY } int CdlDbParser::target_packages(CdlInterpreter interp, int argc, const char* argv[]) { CYG_REPORT_FUNCNAMETYPE("CdlDbParser::target_packages", "result %d"); CYG_REPORT_FUNCARG1XV(argc); CYG_PRECONDITION_CLASSC(interp); std::string name = interp->get_variable(dbparser_targetname); CYG_ASSERTC("" != name); CdlPackagesDatabaseBody::target_data* target = static_cast(interp->get_assoc_data(dbparser_targetdata)); // There should be one argument, a list of valid packages. // The packages command should be used only once if (2 != argc) { CdlParse::report_error(interp, diag_target + name, "`packages' should be followed by a list of known packages."); } else if (0 < target->packages.size()) { CdlParse::report_warning(interp, diag_target + name, "There should be only one list of packages."); } else { int list_count = 0; const char** list_entries = 0; Tcl_Interp* tcl_interp = interp->get_tcl_interpreter(); if (TCL_OK != Tcl_SplitList(tcl_interp, CDL_TCL_CONST_CAST(char*, argv[1]), &list_count, CDL_TCL_CONST_CAST(char***, &list_entries))) { CdlParse::report_error(interp, diag_target + name, Tcl_GetStringResult(tcl_interp)); } else { // Allow for a dummy target spec, just in case it proves useful. if (0 != list_count) { for (int i = 0; i < list_count; i++) { target->packages.push_back(list_entries[i]); } } Tcl_Free((char*)list_entries); } } CYG_REPORT_RETVAL(TCL_OK); return TCL_OK; } // Syntax: enable { opt1 opt2 ... } // For example: enable { CYGPKG_HAL_ARM_CL7xxx_7211 } int CdlDbParser::target_enable(CdlInterpreter interp, int argc, const char* argv[]) { CYG_REPORT_FUNCNAMETYPE("CdlDbParser::target_enable", "result %d"); CYG_REPORT_FUNCARG1XV(argc); CYG_PRECONDITION_CLASSC(interp); std::string name = interp->get_variable(dbparser_targetname); CYG_ASSERTC("" != name); CdlPackagesDatabaseBody::target_data* target = static_cast(interp->get_assoc_data(dbparser_targetdata)); // There should be one argument, a list of valid flags. if (2 != argc) { CdlParse::report_error(interp, diag_target + name, "`enable' should be followed by a list of CDL options."); } else { int list_count = 0; const char** list_entries = 0; Tcl_Interp* tcl_interp = interp->get_tcl_interpreter(); if (TCL_OK != Tcl_SplitList(tcl_interp, CDL_TCL_CONST_CAST(char*, argv[1]), &list_count, CDL_TCL_CONST_CAST(char***, &list_entries))) { CdlParse::report_error(interp, diag_target + name, Tcl_GetStringResult(tcl_interp)); } else { for (int i = 0; i < list_count; i++) { target->enable.push_back(list_entries[i]); } Tcl_Free((char *) list_entries); } } CYG_REPORT_RETVAL(TCL_OK); return TCL_OK; } // Syntax: disable { opt1 opt2 ... } // For example: disable { CYGPKG_HAL_ARM_CL7xxx_7111 } int CdlDbParser::target_disable(CdlInterpreter interp, int argc, const char* argv[]) { CYG_REPORT_FUNCNAMETYPE("CdlDbParser::target_disable", "result %d"); CYG_REPORT_FUNCARG1XV(argc); CYG_PRECONDITION_CLASSC(interp); std::string name = interp->get_variable(dbparser_targetname); CYG_ASSERTC("" != name); CdlPackagesDatabaseBody::target_data* target = static_cast(interp->get_assoc_data(dbparser_targetdata)); // There should be one argument, a list of valid flags. if (2 != argc) { CdlParse::report_error(interp, diag_target + name, "`disable' should be followed by a list of CDL options."); } else { int list_count = 0; const char** list_entries = 0; Tcl_Interp* tcl_interp = interp->get_tcl_interpreter(); if (TCL_OK != Tcl_SplitList(tcl_interp, CDL_TCL_CONST_CAST(char*, argv[1]), &list_count, CDL_TCL_CONST_CAST(char***, &list_entries))) { CdlParse::report_error(interp, diag_target + name, Tcl_GetStringResult(tcl_interp)); } else { for (int i = 0; i < list_count; i++) { target->disable.push_back(list_entries[i]); } Tcl_Free((char *) list_entries); } } CYG_REPORT_RETVAL(TCL_OK); return TCL_OK; } // Syntax: set_value