The createBertosProject function now creates the project folder, copies the sources...
[bertos.git] / wizard / bertos_utils.py
1 #!/usr/bin/env python
2 # encoding: utf-8
3 #
4 # Copyright 2008 Develer S.r.l. (http://www.develer.com/)
5 # All rights reserved.
6 #
7 # $Id:$
8 #
9 # Author: Lorenzo Berni <duplo@develer.com>
10 #
11
12 import os
13 import fnmatch
14 import glob
15 import re
16 import shutil
17
18 import const
19
20 def isBertosDir(directory):
21    return os.path.exists(directory + "/VERSION")
22
23 def bertosVersion(directory):
24    return open(directory + "/VERSION").readline().strip()
25
26 def createBertosProject(projectInfos):
27     directory = projectInfos.info("PROJECT_PATH")
28     sourcesDir = projectInfos.info("SOURCES_PATH")
29     if not os.path.isdir(directory):
30         os.mkdir(directory)
31     f = open(directory + "/project.bertos", "w")
32     f.write(repr(projectInfos))
33     f.close()
34     ## Destination source dir
35     srcdir = directory + "/bertos"
36     shutil.rmtree(srcdir, True)
37     shutil.copytree(sourcesDir + "/bertos", srcdir)
38     ## Destination makefile
39     makefile = directory + "/Makefile"
40     if os.path.exists(makefile):
41         os.remove(makefile)
42     shutil.copy(sourcesDir + "/Makefile", makefile)
43     ## Destination project dir
44     prjdir = directory + "/" + os.path.basename(directory)
45     shutil.rmtree(prjdir, True)
46     os.mkdir(prjdir)
47     ## Destination configurations files
48     cfgdir = prjdir + "/cfg"
49     shutil.rmtree(cfgdir, True)
50     os.mkdir(cfgdir)
51     for key, value in projectInfos.info("CONFIGURATIONS").items():
52         string = open(sourcesDir + "/" + key, "r").read()
53         for parameter, infos in value.items():
54             value = infos["value"]
55             if "long" in infos["informations"].keys() and infos["informations"]["long"]:
56                 value += "L"
57             string = sub(string, parameter, value)
58         f = open(cfgdir + "/" + os.path.basename(key), "w")
59         f.write(string)
60         f.close()
61
62 def getSystemPath():
63     path = os.environ["PATH"]
64     if os.name == "nt":
65         path = path.split(";")
66     else:
67         path = path.split(":")
68     return path
69
70 def findToolchains(pathList):
71     toolchains = []
72     for element in pathList:
73         for toolchain in glob.glob(element+ "/" + const.GCC_NAME):
74             if not os.path.islink(toolchain):
75                 toolchains.append(toolchain)
76     return list(set(toolchains))
77
78 def getToolchainInfo(output):
79     info = {}
80     expr = re.compile("Target: .*")
81     target = expr.findall(output)
82     if len(target) == 1:
83         info["target"] = target[0].split("Target: ")[1]
84     expr = re.compile("gcc version [0-9,.]*")
85     version = expr.findall(output)
86     if len(version) == 1:
87         info["version"] = version[0].split("gcc version ")[1]
88     expr = re.compile("gcc version [0-9,.]* \(.*\)")
89     build = expr.findall(output)
90     if len(build) == 1:
91         build = build[0].split("gcc version ")[1]
92         build = build[build.find("(") + 1 : build.find(")")]
93         info["build"] = build
94     expr = re.compile("Configured with: .*")
95     configured = expr.findall(output)
96     if len(configured) == 1:
97         info["configured"] = configured[0].split("Configured with: ")[1]
98     expr = re.compile("Thread model: .*")
99     thread = expr.findall(output)
100     if len(thread) == 1:
101         info["thread"] = thread[0].split("Thread model: ")[1]
102     return info
103
104 def findDefinitions(ftype, path):
105     L = os.walk(path)
106     for element in L:
107         for filename in element[2]:
108             if fnmatch.fnmatch(filename, ftype):
109                 yield (filename, element[0])
110
111 def loadCpuInfos(path):
112     cpuInfos = []
113     for definition in findDefinitions(const.CPU_DEFINITION, path):
114         cpuInfos.append(getInfos(definition))
115     return cpuInfos
116
117 def getInfos(definition):
118     D = {}
119     D.update(const.CPU_DEF)
120     def include(filename, dict = D, directory=definition[1]):
121         execfile(directory + "/" + filename, {}, D)
122     D["include"] = include
123     include(definition[0], D)
124     D["CPU_NAME"] = definition[0].split(".")[0]
125     D["DEFINITION_PATH"] = definition[1] + "/" + definition[0]
126     del D["include"]
127     return D
128
129 def getDefinitionBlocks(text):
130     """
131     Take a text and return a list of tuple (description, name-value).
132     """
133     block = []
134     block_tmp = re.findall(r"/\*{2}\s*([^*]*\*(?:[^/*][^*]*\*+)*)/\s*#define\s+((?:[^/]*?/?)+)\s*?(?:/{2,3}[^<].*?)?$", text, re.MULTILINE)
135     for comment, define in block_tmp:
136         block.append((" ".join(re.findall(r"^\s*\*?\s*(.*?)\s*?(?:/{2}.*?)?$", comment, re.MULTILINE)).strip(), define))
137     block += re.findall(r"/{3}\s*([^<].*?)\s*#define\s+((?:[^/]*?/?)+)\s*?(?:/{2,3}[^<].*?)?$", text, re.MULTILINE)
138     block += [(comment, define) for define, comment in re.findall(r"#define\s*(.*?)\s*/{3}<\s*(.+?)\s*?(?:/{2,3}[^<].*?)?$", text, re.MULTILINE)]
139     return block
140
141 def formatParamNameValue(text):
142     """
143     Take the given string and return a tuple with the name of the parameter in the first position
144     and the value in the second.
145     """
146     block = re.findall("\s*([^\s]+)\s*(.+?)\s*$", text, re.MULTILINE)
147     return block[0]
148
149 def getDescriptionInformations(text): 
150     """ 
151     Take the doxygen comment and strip the wizard informations, returning the tuple 
152     (comment, wizard_informations) 
153     """ 
154     index = text.find("$WIZARD") 
155     if index != -1: 
156         exec(text[index + 1:]) 
157         informations = WIZARD 
158         return text[:index].strip(), informations
159     else:
160         return text.strip(), {}
161
162 def loadConfigurationInfos(path):
163     """
164     Return the module configurations found in the given file as a dict with the
165     parameter name as key and a dict containig the fields above as value:
166         "value": the value of the parameter
167         "description": the description of the parameter
168         "informations": a dict containig optional informations:
169             "type": "int" | "boolean" | "enum"
170             "min": the minimum value for integer parameters
171             "max": the maximum value for integer parameters
172             "long": boolean indicating if the num is a long
173             "value_list": the name of the enum for enum parameters
174     """
175     configurationInfos = {}
176     for comment, define in getDefinitionBlocks(open(path, "r").read()):
177         name, value = formatParamNameValue(define)
178         description, informations = getDescriptionInformations(comment)
179         configurationInfos[name] = {}
180         configurationInfos[name]["value"] = value
181         configurationInfos[name]["informations"] = informations
182         configurationInfos[name]["description"] = description
183     return configurationInfos
184
185 def loadModuleInfos(path):
186     """
187     Return the module infos found in the given file as a dict with the module
188     name as key and a dict containig the fields above as value or an empty dict
189     if the given file is not a BeRTOS module:
190         "depends": a list of modules needed by this module
191         "configuration": the cfg_*.h with the module configurations
192         "description": a string containing the brief description of doxygen
193         "enabled": contains False but the wizard will change if the user select
194         the module
195     """
196     moduleInfos = {}
197     string = open(path, "r").read()
198     commentList = re.findall(r"/\*{2}\s*([^*]*\*(?:[^/*][^*]*\*+)*)/", string)
199     commentList = [" ".join(re.findall(r"^\s*\*?\s*(.*?)\s*?(?:/{2}.*?)?$", comment, re.MULTILINE)).strip() for comment in commentList]
200     for comment in commentList:
201         index = comment.find("$WIZARD_MODULE")
202         if index != -1:
203             exec(comment[index + 1:])
204             moduleInfos[WIZARD_MODULE["name"]] = {"depends": WIZARD_MODULE["depends"],
205                                                     "configuration": WIZARD_MODULE["configuration"],
206                                                     "description": "",
207                                                     "enabled": False}
208             return moduleInfos
209     return {}
210
211 def loadModuleInfosDict(path):
212     """
213     Return the dict containig all the modules
214     """
215     moduleInfosDict = {}
216     for filename, path in findDefinitions("*.h", path):
217         moduleInfosDict.update(loadModuleInfos(path + "/" + filename))
218     return moduleInfosDict
219
220 def loadDefineLists(path):
221     """
222     Return a dict with the name of the list as key and a list of string as value
223     """
224     string = open(path, "r").read()
225     commentList = re.findall(r"/\*{2}\s*([^*]*\*(?:[^/*][^*]*\*+)*)/", string)
226     commentList = [" ".join(re.findall(r"^\s*\*?\s*(.*?)\s*?(?:/{2}.*?)?$", comment, re.MULTILINE)).strip() for comment in commentList]
227     listDict = {}
228     for comment in commentList:
229         index = comment.find("$WIZARD_LIST")
230         if index != -1:
231             exec(comment[index + 1:])
232             listDict.update(WIZARD_LIST)
233     return listDict
234
235 def loadDefineListsDict(path):
236     """
237     Return the dict containing all the define lists
238     """
239     defineListsDict = {}
240     for filename, path in findDefinitions("*.h", path):
241         defineListsDict.update(loadDefineLists(path + "/" + filename))
242     return defineListsDict
243
244 def sub(string, parameter, value):
245     """
246     Substitute the given value at the given parameter define in the given string
247     """
248     return re.sub(r"(?P<define>#define\s+" + parameter + r"\s+)([^\s]+)", r"\g<define>" + value, string)