Typo
[bertos.git] / wizard / bertos_utils.py
index 5bf0b4306204ba1db4dbf89e7a2dc2ff8f6f6663..ce526f013b5ed1f6f4ae6517ea88590f9301391b 100644 (file)
@@ -1,8 +1,32 @@
 #!/usr/bin/env python
 # encoding: utf-8
 #
+# This file is part of BeRTOS.
+#
+# Bertos 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+#
+# As a special exception, you may use this file as part of a free software
+# library without restriction.  Specifically, if other files instantiate
+# templates or use macros or inline functions from this file, or you compile
+# this file and link it with other files to produce an executable, this
+# file does not by itself cause the resulting executable to be covered by
+# the GNU General Public License.  This exception does not however
+# invalidate any other reasons why the executable file might be covered by
+# the GNU General Public License.
+#
 # Copyright 2008 Develer S.r.l. (http://www.develer.com/)
-# All rights reserved.
 #
 # $Id$
 #
@@ -21,6 +45,9 @@ import pickle
 import const
 import plugins
 import DefineException
+import BProject
+
+from LoadException import VersionException, ToolchainException
 
 def isBertosDir(directory):
    return os.path.exists(directory + "/VERSION")
@@ -28,50 +55,153 @@ def isBertosDir(directory):
 def bertosVersion(directory):
    return open(directory + "/VERSION").readline().strip()
 
-def createBertosProject(project_info):
+def loadBertosProject(project_file, info_dict):
+    project_data = pickle.loads(open(project_file, "r").read())
+    project_info = BProject.BProject()
+    project_info.setInfo("PROJECT_PATH", os.path.dirname(project_file))
+    if "SOURCES_PATH" in info_dict:
+        project_data["SOURCES_PATH"] = info_dict["SOURCES_PATH"]
+    if os.path.exists(project_data["SOURCES_PATH"]):
+        project_info.setInfo("SOURCES_PATH", project_data["SOURCES_PATH"])
+    else:
+        raise VersionException(project_info)
+    loadSourceTree(project_info)
+    cpu_name = project_data["CPU_NAME"]
+    project_info.setInfo("CPU_NAME", cpu_name)
+    cpu_info = loadCpuInfos(project_info)
+    for cpu in cpu_info:
+        if cpu["CPU_NAME"] == cpu_name:
+            project_info.setInfo("CPU_INFOS", cpu)
+            break
+    tag_list = getTagSet(cpu_info)
+    # Create, fill and store the dict with the tags
+    tag_dict = {}
+    for element in tag_list:
+        tag_dict[element] = False
+    infos = project_info.info("CPU_INFOS")
+    for tag in tag_dict:
+        if tag in infos["CPU_TAGS"] + [infos["CPU_NAME"], infos["TOOLCHAIN"]]:
+            tag_dict[tag] = True
+        else:
+            tag_dict[tag] = False
+    project_info.setInfo("ALL_CPU_TAGS", tag_dict)
+    if "TOOLCHAIN" in info_dict:
+        project_data["TOOLCHAIN"] = info_dict["TOOLCHAIN"]
+    if os.path.exists(project_data["TOOLCHAIN"]["path"]):
+        project_info.setInfo("TOOLCHAIN", project_data["TOOLCHAIN"])
+    else:
+        raise ToolchainException(project_info)
+    project_info.setInfo("SELECTED_FREQ", project_data["SELECTED_FREQ"])
+    project_info.setInfo("OUTPUT", project_data["OUTPUT"])
+    loadModuleData(project_info, True)
+    setEnabledModules(project_info, project_data["ENABLED_MODULES"])
+    return project_info
+
+def setEnabledModules(project_info, enabled_modules):
+    modules = project_info.info("MODULES")
+    files = {}
+    for module, information in modules.items():
+        information["enabled"] = module in enabled_modules
+       for dependency in information["depends"]:
+            if not dependency in modules:
+               if dependency in files:
+                   files[dependency] += 1
+               else:
+                   files[dependency] = 1
+    project_info.setInfo("MODULES", modules)
+    project_info.setInfo("FILES", files)
+
+def enabledModules(project_info):
+    enabled_modules = []
+    for name, module in project_info.info("MODULES").items():
+        if module["enabled"]:
+            enabled_modules.append(name)
+    return enabled_modules
+
+def mergeSources(srcdir, new_sources, old_sources):
+    # The current mergeSources function provide only a raw copy of the sources in the
+    # created project.
+    #
+    # TODO: implement the three way merge algorithm
+    #
+    shutil.rmtree(srcdir, True)
+    copytree.copytree(os.path.join(new_sources, "bertos"), srcdir, ignore_list=const.IGNORE_LIST)
+
+def projectFileGenerator(project_info):
+    directory = project_info.info("PROJECT_PATH")
+    project_data = {}
+    enabled_modules = []
+    for module, information in project_info.info("MODULES").items():
+        if information["enabled"]:
+            enabled_modules.append(module)
+    project_data["ENABLED_MODULES"] = enabled_modules
+    project_data["SOURCES_PATH"] = project_info.info("SOURCES_PATH")
+    project_data["TOOLCHAIN"] = project_info.info("TOOLCHAIN")
+    project_data["CPU_NAME"] = project_info.info("CPU_NAME")
+    project_data["SELECTED_FREQ"] = project_info.info("SELECTED_FREQ")
+    project_data["OUTPUT"] = project_info.info("OUTPUT")
+    return pickle.dumps(project_data)
+
+def createBertosProject(project_info, edit=False):
     directory = project_info.info("PROJECT_PATH")
     sources_dir = project_info.info("SOURCES_PATH")
-    if not os.path.isdir(directory):
-        os.mkdir(directory)
+    old_sources_dir = project_info.info("OLD_SOURCES_PATH")
+    if not edit:
+        if os.path.isdir(directory):
+            shutil.rmtree(directory, True)        
+        os.makedirs(directory)
+    # Write the project file
     f = open(directory + "/project.bertos", "w")
-    f.write(pickle.dumps(project_info))
+    f.write(projectFileGenerator(project_info))
     f.close()
     # Destination source dir
     srcdir = directory + "/bertos"
-    shutil.rmtree(srcdir, True)
-    copytree.copytree(sources_dir + "/bertos", srcdir, ignore_list=const.IGNORE_LIST)
+    if not edit:
+        # If not in editing mode it copies all the bertos sources in the /bertos subdirectory of the project
+        shutil.rmtree(srcdir, True)
+        copytree.copytree(sources_dir + "/bertos", srcdir, ignore_list=const.IGNORE_LIST)
+    elif old_sources_dir:
+        # If in editing mode it merges the current bertos sources with the selected ones
+        # TODO: implement the three way merge algotihm
+        #
+        mergeSources(srcdir, sources_dir, old_sources_dir)
     # Destination makefile
     makefile = directory + "/Makefile"
-    if os.path.exists(makefile):
-        os.remove(makefile)
     makefile = open("mktemplates/Makefile").read()
     makefile = makefileGenerator(project_info, makefile)
     open(directory + "/Makefile", "w").write(makefile)
     # Destination project dir
     prjdir = directory + "/" + os.path.basename(directory)
-    shutil.rmtree(prjdir, True)
-    os.mkdir(prjdir)
+    if not edit:
+        shutil.rmtree(prjdir, True)
+        os.mkdir(prjdir)
     # Destination hw files
     hwdir = prjdir + "/hw"
-    shutil.rmtree(hwdir, True)
-    os.mkdir(hwdir)
+    if not edit:
+        shutil.rmtree(hwdir, True)
+        os.mkdir(hwdir)
     # Copy all the hw files
     for module, information in project_info.info("MODULES").items():
         for hwfile in information["hw"]:
             string = open(sources_dir + "/" + hwfile, "r").read()
-            open(hwdir + "/" + os.path.basename(hwfile), "w").write(string)
+            hwfile_path = hwdir + "/" + os.path.basename(hwfile)
+            if not edit or not os.path.exists(hwfile_path):
+                # If not in editing mode it copies all the hw files. If in
+                # editing mode it copies only the files that don't exist yet
+                open(hwdir + "/" + os.path.basename(hwfile), "w").write(string)
     # Destination configurations files
     cfgdir = prjdir + "/cfg"
-    shutil.rmtree(cfgdir, True)
-    os.mkdir(cfgdir)
-    # Set to 1 the autoenabled for enabled modules
+    if not edit:
+        shutil.rmtree(cfgdir, True)
+        os.mkdir(cfgdir)
+    # Set properly the autoenabled parameters
     for module, information in project_info.info("MODULES").items():
-        if information["enabled"] and "configuration" in information and information["configuration"] != "":
+        if "configuration" in information and information["configuration"] != "":
             configurations = project_info.info("CONFIGURATIONS")
             configuration = configurations[information["configuration"]]
             for start, parameter in configuration["paramlist"]:
-                if "type" in configuration[parameter] and configuration[parameter]["type"] == "autoenabled":
-                    configuration[parameter]["value"] = "1"
+                if "type" in configuration[parameter]["informations"] and configuration[parameter]["informations"]["type"] == "autoenabled":
+                    configuration[parameter]["value"] = "1" if information["enabled"] else "0"
             project_info.setInfo("CONFIGURATIONS", configurations)
     # Copy all the configuration files
     for configuration, information in project_info.info("CONFIGURATIONS").items():
@@ -87,36 +217,44 @@ def createBertosProject(project_info):
         f = open(cfgdir + "/" + os.path.basename(configuration), "w")
         f.write(string)
         f.close()
-    # Destinatio mk file
-    makefile = open("mktemplates/template.mk", "r").read()
+    if not edit:
+        # Destination user mk file (only on project creation)
+        makefile = open("mktemplates/template.mk", "r").read()
+        makefile = mkGenerator(project_info, makefile)
+        open(prjdir + "/" + os.path.basename(prjdir) + ".mk", "w").write(makefile)
+    # Destination wizard mk file
+    makefile = open("mktemplates/template_wiz.mk", "r").read()
     makefile = mkGenerator(project_info, makefile)
-    open(prjdir + "/" + os.path.basename(prjdir) + ".mk", "w").write(makefile)
+    open(prjdir + "/" + os.path.basename(prjdir) + "_wiz.mk", "w").write(makefile)
     # Destination main.c file
-    main = open("srctemplates/main.c", "r").read()
-    open(prjdir + "/main.c", "w").write(main)
+    if not edit:
+        main = open("srctemplates/main.c", "r").read()
+        open(prjdir + "/main.c", "w").write(main)
     # Files for selected plugins
     relevants_files = {}
     for plugin in project_info.info("OUTPUT"):
-        module = getattr(__import__("plugins", {}, {}, [plugin]), plugin)
+        module = loadPlugin(plugin)
         relevants_files[plugin] = module.createProject(project_info)
-    project_info.setInfo("RELEVANTS_FILES", relevants_files)
+    project_info.setInfo("RELEVANT_FILES", relevants_files)
 
+def loadPlugin(plugin):
+    """
+    Returns the given plugin module.
+    """
+    return getattr(__import__("plugins", {}, {}, [plugin]), plugin)
+    
 def mkGenerator(project_info, makefile):
     """
     Generates the mk file for the current project.
     """
     mk_data = {}
     mk_data["$pname"] = os.path.basename(project_info.info("PROJECT_PATH"))
-    mk_data["$cpuflag"] = project_info.info("CPU_INFOS")["CPU_FLAG_NAME"]
-    mk_data["$cpuname"] = project_info.info("CPU_INFOS")["CORE_CPU"]
     mk_data["$cpuclockfreq"] = project_info.info("SELECTED_FREQ")
-    mk_data["$cflags"] = " ".join(project_info.info("CPU_INFOS")["C_FLAGS"])
-    mk_data["$ldflags"] = " ".join(project_info.info("CPU_INFOS")["LD_FLAGS"])
-    mk_data["$cppflags"] = " ".join(project_info.info("CPU_INFOS")["CPP_FLAGS"])
-    mk_data["$cppaflags"] = " ".join(project_info.info("CPU_INFOS")["CPPA_FLAGS"])
-    mk_data["$cxxflags"] = " ".join(project_info.info("CPU_INFOS")["CXX_FLAGS"])
-    mk_data["$asflags"] = " ".join(project_info.info("CPU_INFOS")["AS_FLAGS"])
-    mk_data["$arflags"] = " ".join(project_info.info("CPU_INFOS")["AR_FLAGS"])
+    cpu_mk_parameters = []
+    for key, value in project_info.info("CPU_INFOS").items():
+        if key.startswith(const.MK_PARAM_ID):
+            cpu_mk_parameters.append("%s = %s" %(key.replace("MK", mk_data["$pname"]), value))
+    mk_data["$cpuparameters"] = "\n".join(cpu_mk_parameters)
     mk_data["$csrc"], mk_data["$pcsrc"], mk_data["$cppasrc"], mk_data["$cxxsrc"], mk_data["$asrc"], mk_data["$constants"] = csrcGenerator(project_info)
     mk_data["$prefix"] = replaceSeparators(project_info.info("TOOLCHAIN")["path"].split("gcc")[0])
     mk_data["$suffix"] = replaceSeparators(project_info.info("TOOLCHAIN")["path"].split("gcc")[1])
@@ -131,8 +269,8 @@ def makefileGenerator(project_info, makefile):
     Generate the Makefile for the current project.
     """
     # TODO write a general function that works for both the mk file and the Makefile
-    while makefile.find("project_name") != -1:
-        makefile = makefile.replace("project_name", os.path.basename(project_info.info("PROJECT_PATH")))
+    while makefile.find("$pname") != -1:
+        makefile = makefile.replace("$pname", os.path.basename(project_info.info("PROJECT_PATH")))
     return makefile
 
 def csrcGenerator(project_info):
@@ -193,12 +331,17 @@ def csrcGenerator(project_info):
         cxxsrc.append(file)
     for file in project_info.info("CPU_INFOS")["ASRC"]:
         asrc.append(file)
+    csrc = set(csrc)
     csrc = " \\\n\t".join(csrc) + " \\"
+    pcsrc = set(pcsrc)
     pcsrc = " \\\n\t".join(pcsrc) + " \\"
+    cppasrc = set(cppasrc)
     cppasrc = " \\\n\t".join(cppasrc) + " \\"
+    cxxsrc = set(cxxsrc)
     cxxsrc = " \\\n\t".join(cxxsrc) + " \\"
+    asrc = set(asrc)
     asrc = " \\\n\t".join(asrc) + " \\"
-    constants = "\n".join([os.path.basename(project_info.info("PROJECT_PATH")) + "_" + key + " = " + str(value) for key, value in constants.items()])
+    constants = "\n".join([os.path.basename(project_info.info("PROJECT_PATH")) + "_" + key + " = " + unicode(value) for key, value in constants.items()])
     return csrc, pcsrc, cppasrc, cxxsrc, asrc, constants
 
 def findModuleFiles(module, project_info):
@@ -283,6 +426,10 @@ def getToolchainInfo(output):
         info["thread"] = thread[0].split("Thread model: ")[1]
     return info
 
+def getToolchainName(toolchain_info):
+    name = "GCC " + toolchain_info["version"] + " - " + toolchain_info["target"].strip()
+    return name
+
 def loadSourceTree(project):
     fileList = [f for f in os.walk(project.info("SOURCES_PATH"))]
     project.setInfo("FILE_LIST", fileList)
@@ -307,7 +454,6 @@ def getTagSet(cpu_info):
     for cpu in cpu_info:
         tag_set |= set([cpu["CPU_NAME"]])
         tag_set |= set(cpu["CPU_TAGS"])
-        tag_set |= set([cpu["CORE_CPU"]])
         tag_set |= set([cpu["TOOLCHAIN"]])
     return tag_set
         
@@ -365,8 +511,7 @@ def loadModuleDefinition(first_comment):
             del module_definition["module_description"]
         if const.MODULE_DEFINITION["module_harvard"] in module_definition:
             harvard = module_definition[const.MODULE_DEFINITION["module_harvard"]]
-            if harvard == "both" or harvard == "pgm_memory":
-                module_dict[module_name]["harvard"] = harvard
+            module_dict[module_name]["harvard"] = harvard
             del module_definition[const.MODULE_DEFINITION["module_harvard"]]
         if const.MODULE_DEFINITION["module_hw"] in module_definition:
             hw = module_definition[const.MODULE_DEFINITION["module_hw"]]
@@ -467,7 +612,7 @@ def getDefinitionBlocks(text):
         block.append(([comment], define, start))
     return block
 
-def loadModuleData(project):
+def loadModuleData(project, edit=False):
     module_info_dict = {}
     list_info_dict = {}
     configuration_info_dict = {}
@@ -492,6 +637,14 @@ def loadModuleData(project):
                         configuration_info[configuration] = loadConfigurationInfos(project.info("SOURCES_PATH") + "/" + configuration)
                     except ParseError, err:
                         raise DefineException.ConfigurationDefineException(project.info("SOURCES_PATH") + "/" + configuration, err.line_number, err.line)
+                    if edit:
+                        try:
+                            path = os.path.basename(project.info("PROJECT_PATH"))
+                            directory = project.info("PROJECT_PATH")
+                            user_configuration = loadConfigurationInfos(directory + "/" + configuration.replace("bertos", path))
+                            configuration_info[configuration] = updateConfigurationValues(configuration_info[configuration], user_configuration)
+                        except ParseError, err:
+                            raise DefineException.ConfigurationDefineException(directory + "/" + configuration.replace("bertos", path))
             module_info_dict.update(module_dict)
             configuration_info_dict.update(configuration_info)
             if to_be_parsed:
@@ -533,6 +686,7 @@ def loadConfigurationInfos(path):
             "long": boolean indicating if the num is a long
             "unsigned": boolean indicating if the num is an unsigned
             "value_list": the name of the enum for enum parameters
+            "conditional_deps": the list of conditional dependencies for boolean parameters
     """
     configuration_infos = {}
     configuration_infos["paramlist"] = []
@@ -555,10 +709,24 @@ def loadConfigurationInfos(path):
                 configuration_infos[name]["value"].find("U") != -1):
             configuration_infos[name]["informations"]["unsigned"] = True
             configuration_infos[name]["value"] = configuration_infos[name]["value"].replace("U", "")
+        if "conditional_deps" in configuration_infos[name]["informations"]:
+            if (type(configuration_infos[name]["informations"]["conditional_deps"]) == str or
+                    type(configuration_infos[name]["informations"]["conditional_deps"]) == unicode):
+                configuration_infos[name]["informations"]["conditional_deps"] = (configuration_infos[name]["informations"]["conditional_deps"], )
+            elif type(configuration_infos[name]["informations"]["conditional_deps"]) == tuple:
+                pass
+            else:
+                configuration_infos[name]["informations"]["conditional_deps"] = ()
         configuration_infos[name]["description"] = description
         configuration_infos[name]["brief"] = brief
     return configuration_infos
 
+def updateConfigurationValues(def_conf, user_conf):
+    for param in def_conf["paramlist"]:
+        if param[1] in user_conf and "value" in user_conf[param[1]]:
+            def_conf[param[1]]["value"] = user_conf[param[1]]["value"]
+    return def_conf
+
 def findParameterType(parameter):
     if "value_list" in parameter["informations"]:
         return "enum"
@@ -616,4 +784,4 @@ class ParseError(Exception):
 class SupportedException(Exception):
     def __init__(self, support_string):
         Exception.__init__(self)
-        self.support_string = support_string
\ No newline at end of file
+        self.support_string = support_string