Mercurial > nand-ecoscentric
changeset 2146:bbc54594034a
permtest.cxx
| author | msalter |
|---|---|
| date | Tue, 28 Feb 2006 12:36:34 +0000 |
| parents | accf1e9370f5 |
| children | 751d90a8dae3 |
| files | host/tools/ecostest/common/permtest.cxx |
| diffstat | 1 files changed, 760 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/host/tools/ecostest/common/permtest.cxx @@ -0,0 +1,760 @@ +//####COPYRIGHTBEGIN#### +// +// ---------------------------------------------------------------------------- +// Copyright (C) 1998, 1999, 2000 Red Hat, Inc. +// +// This program 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#### +//========================================================================== +// +// permtest.cxx +// +// Create a configuration based on a .ptest file +// +//========================================================================== +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): bartv +// Contributors: bartv +// Date: 1999-11-05 +// +//####DESCRIPTIONEND#### +//========================================================================== + +#include <cdl.hxx> +#include <cstdio> +#include <cstdlib> + +// ---------------------------------------------------------------------------- +// Statics. +// +// The global configuration is created as the result of a pkgconf command +// when executing the .ptest file +static CdlConfiguration configuration = 0; + +// The database is set up before the script is evaluated. +static CdlPackagesDatabase database = 0; + +// The component repository can come from a command line argument or from +// an environment variable. +static char* component_repository = 0; + +// The name of the .ptest file. This is also the name used for the configuration. +static std::string ptest_file = ""; + +// The target comes from a command line argument, and is needed by +// tcl_pkgconf_command() +std::string target = ""; + +// The startup comes from a command line argument. +static char* startup = 0; + +// ---------------------------------------------------------------------------- +// Diagnostics support +static void error_fn(std::string msg) +{ + fprintf(stderr, "%s\n", msg.c_str()); + exit(EXIT_FAILURE); +} + +static void warn_fn(std::string msg) +{ + fprintf(stderr, "%s\n", msg.c_str()); +} + +// ---------------------------------------------------------------------------- +// Given a database, output the known targets. This happens in response to +// the --targets flag or when the specified target is known. +// It is up to the calling code to exit with a suitable error code. +void +output_targets() +{ + if (0 == database) { + fprintf(stderr, "Internal error, attempt to list targets when there is no database.\n"); + exit(EXIT_FAILURE); + } + const std::vector<std::string>& known_targets = database->get_targets(); + std::vector<std::string>::const_iterator target_i; + + printf("Known targets:\n"); + for (target_i = known_targets.begin(); target_i != known_targets.end(); target_i++) { + printf(" %s\n", target_i->c_str()); + } +} + +// ---------------------------------------------------------------------------- +// Given a component repository and a permutation argument, find the +// corresponding .ptest file and store the result in the global ptest_file +static void find_ptest(CdlInterpreter interp, char* ptest_arg) +{ + if (interp->is_file(ptest_arg)) { + ptest_file = ptest_arg; + return; + } + std::string tmp = std::string(ptest_arg) + ".ptest"; + if (interp->is_file(tmp)) { + ptest_file = tmp; + return; + } + tmp = std::string(component_repository) + "/../testing/pkgtest/permtests/" + ptest_arg; + if (interp->is_file(tmp)) { + ptest_file = tmp; + return; + } + tmp += ".ptest"; + if (interp->is_file(tmp)) { + ptest_file = tmp; + return; + } +} + +// ---------------------------------------------------------------------------- +// Given an alias for a target or package, turn it into the canonical name. +std::string +get_target_canonical_name(std::string alias) +{ + if (0 == database) { + fprintf(stderr, "Internal error, attempt to get target canonical name when there is no database.\n"); + exit(EXIT_FAILURE); + } + + std::string result = ""; + const std::vector<std::string>& known_targets = database->get_targets(); + std::vector<std::string>::const_iterator target_i; + for (target_i = known_targets.begin(); ("" == result) && (target_i != known_targets.end()); target_i++) { + if (alias == *target_i) { + result = alias; + break; + } + + const std::vector<std::string>& aliases = database->get_target_aliases(*target_i); + std::vector<std::string>::const_iterator alias_i; + for (alias_i = aliases.begin(); alias_i != aliases.end(); alias_i++) { + if (alias == *alias_i) { + result = *target_i; + break; + } + } + } + + return result; +} + +std::string +get_package_canonical_name(std::string alias) +{ + if (0 == database) { + fprintf(stderr, "Internal error, attempt to get package canonical name when there is no database.\n"); + exit(EXIT_FAILURE); + } + + std::string result = ""; + const std::vector<std::string>& known_packages = database->get_packages(); + std::vector<std::string>::const_iterator package_i; + for (package_i = known_packages.begin(); ("" == result) && (package_i != known_packages.end()); package_i++) { + if (alias == *package_i) { + result = alias; + break; + } + + const std::vector<std::string>& aliases = database->get_package_aliases(*package_i); + std::vector<std::string>::const_iterator alias_i; + for (alias_i = aliases.begin(); alias_i != aliases.end(); alias_i++) { + if (alias == *alias_i) { + result = *package_i; + break; + } + } + } + + return result; +} + +// ---------------------------------------------------------------------------- +// The pkgconf command is responsible for creating the initial +// configuration. This should use the hardware as per the command-line +// target argument, either the default template or the uITRON package +// depending on the presence of an explicit -disable-uitron or +// --disable-uitron argument, and any additional packages added +// or removed. + +static int +tcl_pkgconf_command(CdlInterpreter interp, int argc, char** argv) +{ + if (0 != configuration) { + interp->set_result("Invalid `pkgconf' command, the configuration already exists."); + return TCL_ERROR; + } + if ("" == target) { + interp->set_result("Internal error, attempt to create a configuration with no known target."); + return TCL_ERROR; + } + if (0 == database) { + interp->set_result("Internal error, attempt to create a configuration before the database has been initialized."); + return TCL_ERROR; + } + if ("" == ptest_file) { + interp->set_result("Internal error, attempt to create a configuration when the .ptest file is unknown."); + return TCL_ERROR; + } + + // Create a new interpreter for the configuration. Re-using the one + // being used for evaluating the .ptest file could cause problems. + CdlInterpreter new_interp = CdlInterpreterBody::make(); + if (0 == new_interp) { + interp->set_result("Unable to create a new Tcl interpreter, out of memory?"); + return TCL_ERROR; + } + + configuration = CdlConfigurationBody::make(ptest_file, database, new_interp); + if (0 == configuration) { + interp->set_result("Unable to create a new configuration, out of memory?"); + return TCL_ERROR; + } + + bool uitron = true; + int i; + for (i = 1; i < argc; i++) { + if ((0 == strcmp(argv[i], "-disable-uitron")) || (0 == strcmp(argv[i], "--disable-uitron"))) { + uitron = false; + break; + } + } + + try { + configuration->set_hardware(target, &error_fn, &warn_fn); + if (uitron) { + configuration->set_template("uitron", "", &error_fn, &warn_fn); + } else { + configuration->set_template("default", "", &error_fn, &warn_fn); + } + + for (i = 1; i < argc; i++) { + char* disable = 0; + char* enable = 0; + + if (0 == strncmp(argv[i], "-disable-", 9)) { + disable = &(argv[i][9]); + } else if (0 == strncmp(argv[i], "--disable-", 10)) { + disable = &(argv[i][10]); + } else if (0 == strncmp(argv[i], "-enable-", 8)) { + enable = &(argv[i][8]); + } else if (0 == strncmp(argv[i], "--enable-", 9)) { + enable = &(argv[i][9]); + } else { + interp->set_result(std::string("Invalid pkgconf argument `") + argv[i] + "'"); + return TCL_ERROR; + } + + if ((0 != disable) && (0 != strcmp(disable, "uitron"))) { + std::string package = get_package_canonical_name(disable); + if ("" == package) { + interp->set_result(std::string("Error in pkgconf command, attempt to disable unknown package `") + + disable + "'"); + return TCL_ERROR; + } + const std::vector<CdlLoadable>& loadables = configuration->get_loadables(); + std::vector<CdlLoadable>::const_iterator package_i; + for (package_i = loadables.begin(); package_i != loadables.end(); package_i++) { + if (package == (*package_i)->get_name()) { + break; + } + } + if (package_i == loadables.end()) { + printf("Warning, pkgconf command, cannot disable %s because it is not loaded in the current configuration", + disable); + } else { + configuration->unload_package(package, false); + } + } + + if (0 != enable) { + std::string package = get_package_canonical_name(enable); + if ("" == package) { + interp->set_result(std::string("Error in pkgconf command, attempt to enable unknown package `") + + enable + "'"); + return TCL_ERROR; + } + const std::vector<CdlLoadable>& loadables = configuration->get_loadables(); + std::vector<CdlLoadable>::const_iterator package_i; + for (package_i = loadables.begin(); package_i != loadables.end(); package_i++) { + if (package == (*package_i)->get_name()) { + break; + } + } + if (package_i != loadables.end()) { + interp->set_result(std::string("Error in pkgconf command, cannot enable `") + enable + + "' because it is already loaded in the current configuration."); + return TCL_ERROR; + } + configuration->load_package(package, "", &error_fn, &warn_fn, false); + } + } + } catch(std::bad_alloc) { + interp->set_result(std::string("Unexpected error, out of memory?")); + return TCL_ERROR; + } catch(CdlInputOutputException e) { + interp->set_result(std::string("File I/O exception: ") + e.get_message()); + return TCL_ERROR; + } catch(CdlParseException e) { + interp->set_result(std::string("Parse error: ") + e.get_message()); + return TCL_ERROR; + } catch(...) { + interp->set_result("Internal error, an unexpected C++ exception was raised"); + return TCL_ERROR; + } + + return TCL_OK; +} + +// ---------------------------------------------------------------------------- +// Other Tcl commands, on the whole these are straightforward. +static int +tcl_header_command(CdlInterpreter interp, int argc, char** argv) +{ + // Modifying a configuration no longer involves editing + // a header file. Instead a single overall configuration + // is updated, and then a savefile is generated. All that + // is necessary is to evaluate the second argument, which + // should be a Tcl script containing enable/disable/value + // commands. + if (3 != argc) { + interp->set_result("Invalid `header' command, expecting two arguments."); + return TCL_ERROR; + } + if (0 == configuration) { + interp->set_result("`header' command detected before the configuration was created."); + return TCL_ERROR; + } + std::string str_result; + int result = interp->eval(argv[2], str_result); + return result; +} + +static int +tcl_makefile_command(CdlInterpreter interp, int argc, char** argv) +{ + // The makefile command was used to modify the compiler flags. + // This happens very differently in the CDL world, and the + // best solution for now is to ignore this command completely. + if (3 != argc) { + interp->set_result("Invalid `makefile' command, expecting two arguments."); + return TCL_ERROR; + } + if (0 == configuration) { + interp->set_result("`header' command detected before the configuration was created."); + return TCL_ERROR; + } + return TCL_OK; +} + +static int +tcl_enable_command(CdlInterpreter interp, int argc, char** argv) +{ + // There should be one argument, the name of a configuration option. + if (2 != argc) { + interp->set_result("Invalid `enable' command, expecting one argument."); + return TCL_ERROR; + } + if (0 == configuration) { + interp->set_result("`enable' command detected before the configuration was created."); + return TCL_ERROR; + } + + CdlNode node = configuration->lookup(argv[1]); + if (0 == node) { + printf("Warning, attempt to enable unknown option %s\n", argv[1]); + return TCL_OK; + } + CdlValuable valuable = dynamic_cast<CdlValuable>(node); + if ((0 == valuable) || !valuable->is_modifiable()) { + printf("Warning, attempt to disable non-modifiable option %s\n", argv[1]); + return TCL_OK; + } + CdlValueFlavor flavor = valuable->get_flavor(); + if ((CdlValueFlavor_Bool != flavor) && (CdlValueFlavor_BoolData != flavor)) { + printf("Warning, attempt to disable non-boolean option %s\n", argv[1]); + return TCL_OK; + } + + valuable->enable(CdlValueSource_User); + return TCL_OK; +} + +static int +tcl_disable_command(CdlInterpreter interp, int argc, char** argv) +{ + // There should be one argument, the name of a configuration option. + if (2 != argc) { + interp->set_result("Invalid `disable' command, expecting one argument."); + return TCL_ERROR; + } + if (0 == configuration) { + interp->set_result("`header' command detected before the configuration was created."); + return TCL_ERROR; + } + + CdlNode node = configuration->lookup(argv[1]); + if (0 == node) { + printf("Warning, attempt to disable unknown option %s\n", argv[1]); + return TCL_OK; + } + CdlValuable valuable = dynamic_cast<CdlValuable>(node); + if ((0 == valuable) || !valuable->is_modifiable()) { + printf("Warning, attempt to disable non-modifiable option %s\n", argv[1]); + return TCL_OK; + } + CdlValueFlavor flavor = valuable->get_flavor(); + if ((CdlValueFlavor_Bool != flavor) && (CdlValueFlavor_BoolData != flavor)) { + printf("Warning, attempt to disable non-boolean option %s\n", argv[1]); + return TCL_OK; + } + + valuable->disable(CdlValueSource_User); + return TCL_OK; +} + +static int +tcl_value_command(CdlInterpreter interp, int argc, char** argv) +{ + // There should be two argument, the name of a configuration option and the new value + if (3 != argc) { + interp->set_result("Invalid `value' command, expecting two arguments."); + return TCL_ERROR; + } + if (0 == configuration) { + interp->set_result("`value' command detected before the configuration was created."); + return TCL_ERROR; + } + + CdlNode node = configuration->lookup(argv[1]); + if (0 == node) { + printf("Warning, attempt to change the value of an unknown option %s\n", argv[1]); + return TCL_OK; + } + CdlValuable valuable = dynamic_cast<CdlValuable>(node); + if ((0 == valuable) || !valuable->is_modifiable()) { + printf("Warning, attempt to change the value of non-modifiable option %s\n", argv[1]); + return TCL_OK; + } + CdlValueFlavor flavor = valuable->get_flavor(); + if ((CdlValueFlavor_BoolData != flavor) && (CdlValueFlavor_Data != flavor)) { + printf("Warning, attempt to change the value of non-data option %s\n", argv[1]); + return TCL_OK; + } + + valuable->set_value(argv[2], CdlValueSource_User); + + // There's no point setting the value for a BoolData but keeping it disabled + if (CdlValueFlavor_BoolData == flavor) { + valuable->enable(CdlValueSource_User); + } + + return TCL_OK; +} + +// ---------------------------------------------------------------------------- +// Specifying the startup. There should be an option CYGHWR_HAL_STARTUP +// or CYG_HAL_STARTUP which can be set to the appropriate value. +static void +process_startup() +{ + if (0 == configuration) { + fprintf(stderr, "Error: attempting to set the startup before a configuration has been created.\n"); + exit(EXIT_FAILURE); + } + if (0 == startup) { + fprintf(stderr, "Error: no startup has been specified.\n"); + exit(EXIT_FAILURE); + } + + CdlNode node = configuration->lookup("CYGHWR_HAL_STARTUP"); + if (0 == node) { + node = configuration->lookup("CYG_HAL_STARTUP"); + } + if (0 == node) { + printf("Warning, there is no option CYGHWR_HAL_STARTUP in the current configuration.\n"); + return; + } + CdlValuable valuable = dynamic_cast<CdlValuable>(node); + if (0 == valuable) { + fprintf(stderr, "Error: CYGHWR_HAL_STARTUP exists but is not an option in the current configuration.\n"); + exit(EXIT_FAILURE); + } + valuable->set_value(startup, CdlValueSource_User); +} + +// ---------------------------------------------------------------------------- +static void +usage(void) +{ + printf("Usage: permtest [options] -target <target> -startup <startup> <ptest>\n"); + printf(" permtest --help\n"); + printf(" permtest --targets\n"); + printf("\n"); + printf(" Valid options are:\n"); + printf(" -srcdir <component repository>\n"); + + // It is up to the calling code to exit with a suitable exit code. +} + +// ---------------------------------------------------------------------------- +int +main(int argc, char** argv) +{ + bool list_targets = false; + bool show_help = false; + char* target_arg = 0; + char* ptest_arg = 0; + + // Time to process all the arguments. + int i; + for (i = 1; i < argc; i++) { + + // If there are any -- arguments, ignore the first - + char* arg = argv[i]; + if (('-' == arg[0]) && ('-' == arg[1])) { + arg = &(arg[1]); + } + + if ((0 == strcmp("-help", arg)) || (0 == strcmp("-H", arg))) { + show_help = true; + continue; + } + + if (0 == strcmp("-targets", arg)) { + list_targets = true; + continue; + } + + if (0 == strncmp("-target", arg, 7)) { + if (0 != target_arg) { + fprintf(stderr, "Only one target should be specified.\n"); + usage(); + exit(EXIT_FAILURE); + } + if ('=' == arg[7]) { + target_arg = &(arg[8]); + } else { + if (++i == argc) { + fprintf(stderr, "Missing argument after -target\n"); + usage(); + exit(EXIT_FAILURE); + } + target_arg = argv[i]; + } + continue; + } + if (0 == strncmp("-srcdir", arg, 7)) { + if (0 != component_repository) { + fprintf(stderr, "The component repository should be specified only once.\n"); + usage(); + exit(EXIT_FAILURE); + } + if ('=' == arg[7]) { + component_repository = &(arg[8]); + } else { + if (++i == argc) { + fprintf(stderr, "Missing argument after -srcdir\n"); + usage(); + exit(EXIT_FAILURE); + } + component_repository = argv[i]; + } + continue; + } + if (0 == strncmp("-startup", argv[i], 8)) { + if (0 != startup) { + fprintf(stderr, "The startup mode should be specified only once.\n"); + usage(); + exit(EXIT_FAILURE); + } + if ('=' == arg[8]) { + startup = &(arg[9]); + } else { + if (++i == argc) { + fprintf(stderr, "Missing argument after -startup\n"); + usage(); + exit(EXIT_FAILURE); + } + startup = argv[i]; + } + continue; + } + + // None of the standard arguments, this had better be a ptest + if (0 == ptest_arg) { + ptest_arg = argv[i]; + continue; + } else { + fprintf(stderr, "Invalid argument %s, only one permutation file should be specified.\n", argv[i]); + usage(); + exit(EXIT_FAILURE); + } + } + + // That takes care of the argument processing, time to do + // something interesting. + if (show_help) { + usage(); + return EXIT_SUCCESS; + } + + // Locate the component repository. This can come from either a + // command-line argument or from an environment variable. The + // component repository is validated by creating a database. + if ((0 == component_repository) || ('\0' == component_repository[0])) { + component_repository = getenv("ECOS_REPOSITORY"); + if (0 == component_repository) { + fprintf(stderr, "The eCos repository location is unknown.\n"); + fprintf(stderr, "Please use a --srcdir command line argument, or set\n"); + fprintf(stderr, "the ECOS_REPOSITORY environment variable.\n"); + exit(EXIT_FAILURE); + } + } + + // Create the Tcl interpreter, to provide ready access to the file + // system and for processing the permutation file itself. + CdlInterpreter interp = CdlInterpreterBody::make(); + if (0 == interp) { + fprintf(stderr, "Internal error, unable to create a Tcl interpreter.\n"); + exit(EXIT_FAILURE); + } + interp->push_context("permtest"); + interp->push_error_fn_ptr(&error_fn); + interp->push_warning_fn_ptr(&warn_fn); + + // Now look for the ptest file. + if ((0 == ptest_arg) || ('\0' == ptest_arg[0])) { + fprintf(stderr, "Please specify a permutation to be tested, e.g. default.ptest\n"); + usage(); + exit(EXIT_FAILURE); + } + find_ptest(interp, ptest_arg); + if ("" == ptest_file) { + fprintf(stderr, "Unable to locate a permutation file corresponding to %s\n", ptest_arg); + exit(EXIT_FAILURE); + } + + try { + database = CdlPackagesDatabaseBody::make(component_repository, &error_fn, &warn_fn); + } catch(std::bad_alloc) { + fprintf(stderr, "Failed to initialize the component repository database, out of memory?\n"); + exit(EXIT_FAILURE); + } catch(CdlInputOutputException e) { + fprintf(stderr, "Failed to initialize the component repository database.\n"); + fprintf(stderr, " %s\n", e.get_message().c_str()); + exit(EXIT_FAILURE); + } catch(...) { + fprintf(stderr, "Internal error, unexpected C++ exception while initializing the component repository database.\n"); + exit(EXIT_FAILURE); + } + + // Once we have a database we can process the target. + if (list_targets) { + output_targets(); + exit(EXIT_SUCCESS); + } + if ((0 == target_arg) || ('\0' == target_arg[0])) { + fprintf(stderr, "Please specify a target using e.g. --target=xyz\n"); + usage(); + exit(EXIT_FAILURE); + } + target = get_target_canonical_name(target_arg); + if ("" == target) { + fprintf(stderr, "Invalid target %s\n", target_arg); + output_targets(); + exit(EXIT_FAILURE); + } + + if ((0 == startup) || ('\0' == startup[0])) { + fprintf(stderr, "Please specify a startup for this target, e.g. rom or ram\n"); + usage(); + exit(EXIT_FAILURE); + } + + interp->add_command("pkgconf", &tcl_pkgconf_command); + interp->add_command("header", &tcl_header_command); + interp->add_command("makefile", &tcl_makefile_command); + interp->add_command("enable", &tcl_enable_command); + interp->add_command("disable", &tcl_disable_command); + interp->add_command("value", &tcl_value_command); + + std::string str_result; + int result = interp->eval_file(ptest_file, str_result); + if (TCL_OK != result) { + fprintf(stderr, "An error occurred while processing the permutation file.\n"); + fprintf(stderr, "%s\n", str_result.c_str()); + exit(EXIT_FAILURE); + } + + // Take care of the startup argument as well. + process_startup(); + + // Perform global conflict resolution, in case some of the + // conflicts were caused by the order in which packages were + // loaded. + configuration->resolve_all_conflicts(); + + // We now have a configuration. Is it valid? + bool valid = true; + const std::list<CdlConflict>& all_conflicts = configuration->get_all_conflicts(); + std::list<CdlConflict>::const_iterator conf_i; + for (conf_i = all_conflicts.begin(); conf_i != all_conflicts.end(); conf_i++) { + // HACK: ignore any conflicts inside interfaces. + CdlNode node = (*conf_i)->get_node(); + CdlInterface interface = dynamic_cast<CdlInterface>(node); + if (0 != interface) { + continue; + } + CdlProperty prop = (*conf_i)->get_property(); + fprintf(stderr, "Conflict: %s %s, property %s\n %s\n", node->get_class_name().c_str(), node->get_name().c_str(), + prop->get_property_name().c_str(), (*conf_i)->get_explanation().c_str()); + valid = false; + } + if (!valid) { + printf("This configuration is invalid and cannot be built.\n"); + exit(EXIT_FAILURE); + } + + // It appears we have a valid configuration. Generate the savefile. + try { + configuration->save("ecos.ecc"); + } catch(std::bad_alloc) { + fprintf(stderr, "Unexpected error while saving configuration, out of memory?\n"); + exit(EXIT_FAILURE); + } catch(CdlInputOutputException e) { + fprintf(stderr, "I/O error while saving configuration.\n"); + fprintf(stderr, "%s\n", e.get_message().c_str()); + exit(EXIT_FAILURE); + } + + // Time to invoke ecosconfig. This needs a component repository environment variable + interp->set_variable("env(ECOS_REPOSITORY)", component_repository); + result = interp->eval("exec ecosconfig tree", str_result); + if (TCL_OK != result) { + fprintf(stderr, "Error when invoking ecosconfig\n"); + fprintf(stderr, "%s\n", str_result.c_str()); + exit(EXIT_FAILURE); + } + + return EXIT_SUCCESS; +}
