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;
+}