4 # Copyright 2008 Develer S.r.l. (http://www.develer.com/)
9 # Author: Lorenzo Berni <duplo@develer.com>
19 import DefineException
21 # Try to use the new parsing module for the module information and the define lists
24 def isBertosDir(directory):
25 return os.path.exists(directory + "/VERSION")
27 def bertosVersion(directory):
28 return open(directory + "/VERSION").readline().strip()
30 def createBertosProject(projectInfos):
31 directory = projectInfos.info("PROJECT_PATH")
32 sourcesDir = projectInfos.info("SOURCES_PATH")
33 if not os.path.isdir(directory):
35 f = open(directory + "/project.bertos", "w")
36 f.write(repr(projectInfos))
38 ## Destination source dir
39 srcdir = directory + "/bertos"
40 shutil.rmtree(srcdir, True)
41 shutil.copytree(sourcesDir + "/bertos", srcdir)
42 ## Destination makefile
43 makefile = directory + "/Makefile"
44 if os.path.exists(makefile):
46 makefile = open("mktemplates/Makefile").read()
47 makefile = makefileGenerator(projectInfos, makefile)
48 open(directory + "/Makefile", "w").write(makefile)
49 ## Destination project dir
50 prjdir = directory + "/" + os.path.basename(directory)
51 shutil.rmtree(prjdir, True)
53 ## Destination configurations files
54 cfgdir = prjdir + "/cfg"
55 shutil.rmtree(cfgdir, True)
57 for key, value in projectInfos.info("CONFIGURATIONS").items():
58 string = open(sourcesDir + "/" + key, "r").read()
59 for parameter, infos in value.items():
60 value = infos["value"]
61 if "unsigned" in infos["informations"].keys() and infos["informations"]["unsigned"]:
63 if "long" in infos["informations"].keys() and infos["informations"]["long"]:
65 string = sub(string, parameter, value)
66 f = open(cfgdir + "/" + os.path.basename(key), "w")
70 makefile = open("mktemplates/template.mk", "r").read()
71 makefile = mkGenerator(projectInfos, makefile)
72 open(prjdir + "/" + os.path.basename(prjdir) + ".mk", "w").write(makefile)
74 def mkGenerator(projectInfos, makefile):
76 Generates the mk file for the current project.
79 mkData["pname"] = os.path.basename(projectInfos.info("PROJECT_PATH"))
80 mkData["cpuname"] = projectInfos.info("CPU_INFOS")["CPU_NAME"]
81 mkData["cflags"] = " ".join(projectInfos.info("CPU_INFOS")["C_FLAGS"])
82 mkData["ldflags"] = " ".join(projectInfos.info("CPU_INFOS")["LD_FLAGS"])
84 while makefile.find(key) != -1:
85 makefile = makefile.replace(key, mkData[key])
88 def makefileGenerator(projectInfos, makefile):
90 Generate the Makefile for the current project.
92 # TODO: write a general function that works for both the mk file and the Makefile
93 while makefile.find("project_name") != -1:
94 makefile = makefile.replace("project_name", os.path.basename(projectInfos.info("PROJECT_PATH")))
98 path = os.environ["PATH"]
100 path = path.split(";")
102 path = path.split(":")
105 def findToolchains(pathList):
107 for element in pathList:
108 for toolchain in glob.glob(element+ "/" + const.GCC_NAME):
109 if not os.path.islink(toolchain):
110 toolchains.append(toolchain)
111 return list(set(toolchains))
113 def getToolchainInfo(output):
115 expr = re.compile("Target: .*")
116 target = expr.findall(output)
118 info["target"] = target[0].split("Target: ")[1]
119 expr = re.compile("gcc version [0-9,.]*")
120 version = expr.findall(output)
121 if len(version) == 1:
122 info["version"] = version[0].split("gcc version ")[1]
123 expr = re.compile("gcc version [0-9,.]* \(.*\)")
124 build = expr.findall(output)
126 build = build[0].split("gcc version ")[1]
127 build = build[build.find("(") + 1 : build.find(")")]
128 info["build"] = build
129 expr = re.compile("Configured with: .*")
130 configured = expr.findall(output)
131 if len(configured) == 1:
132 info["configured"] = configured[0].split("Configured with: ")[1]
133 expr = re.compile("Thread model: .*")
134 thread = expr.findall(output)
136 info["thread"] = thread[0].split("Thread model: ")[1]
139 def loadSourceTree(project):
140 fileList = [f for f in os.walk(project.info("SOURCES_PATH"))]
141 project.setInfo("FILE_LIST", fileList)
143 def findDefinitions(ftype, project):
144 L = project.info("FILE_LIST")
147 for filename in element[2]:
148 if fnmatch.fnmatch(filename, ftype):
149 definitions.append((filename, element[0]))
152 def loadCpuInfos(project):
154 for definition in findDefinitions(const.CPU_DEFINITION, project):
155 cpuInfos.append(getInfos(definition))
158 def getInfos(definition):
160 D.update(const.CPU_DEF)
161 def include(filename, dict = D, directory=definition[1]):
162 execfile(directory + "/" + filename, {}, D)
163 D["include"] = include
164 include(definition[0], D)
165 D["CPU_NAME"] = definition[0].split(".")[0]
166 D["DEFINITION_PATH"] = definition[1] + "/" + definition[0]
170 def loadModuleData(project):
173 configurationInfoDict = {}
174 for filename, path in findDefinitions("*.h", project):
175 commentList = newParser.getCommentList(open(path + "/" + filename, "r").read())
176 if len(commentList) > 0:
178 configurationInfo = {}
180 toBeParsed, moduleDict = newParser.loadModuleDefinition(commentList[0])
181 except newParser.ParseError, err:
182 print "error in file %s. line: %d - statement %s" % (path + "/" + filename, err.line_number, err.line)
186 for module, information in moduleDict.items():
187 if "configuration" in information.keys() and len(information["configuration"]):
188 configuration = moduleDict[module]["configuration"]
189 configurationInfo[configuration] = loadConfigurationInfos(project.info("SOURCES_PATH") + "/" + configuration)
190 moduleInfoDict.update(moduleDict)
191 configurationInfoDict.update(configurationInfo)
194 listDict = newParser.loadDefineLists(commentList[1:])
195 listInfoDict.update(listDict)
196 except newParser.ParseError, err:
197 print "error in file %s. line: %d - statement %s" % (path + "/" + filename, err.line_number, err.line)
201 for filename, path in findDefinitions("*_" + project.info("CPU_INFOS")["TOOLCHAIN"] + ".h", project):
202 commentList = newParser.getCommentList(open(path + "/" + filename, "r").read())
203 listInfoDict.update(newParser.loadDefineLists(commentList))
204 project.setInfo("MODULES", moduleInfoDict)
205 project.setInfo("LISTS", listInfoDict)
206 project.setInfo("CONFIGURATIONS", configurationInfoDict)
209 def loadModuleData_old(project):
211 Loads all the module data, like module definition, list definition, and module configurations
212 int the given BProject, using the SOURCES_PATH information from this as the base for find the
217 configurationsInfoDict = {}
218 for filename, path in findDefinitions("*.h", project):
219 moduleInfos, listInfos, configurationInfos= loadModuleInfos(path + "/" + filename)
220 moduleInfosDict.update(moduleInfos)
221 listInfosDict.update(listInfos)
222 for configuration in configurationInfos.keys():
223 configurationsInfoDict[configuration] = loadConfigurationInfos(project.info("SOURCES_PATH") + "/" + configuration)
224 for filename, path in findDefinitions("*_" + project.info("CPU_INFOS")["TOOLCHAIN"] + ".h", project):
225 listInfosDict.update(loadDefineLists(path + "/" + filename))
226 project.setInfo("MODULES", moduleInfosDict)
227 project.setInfo("LISTS", listInfosDict)
228 project.setInfo("CONFIGURATIONS", configurationsInfoDict)
230 def getDefinitionBlocks(text):
232 Take a text and return a list of tuple (description, name-value).
235 block_tmp = re.findall(r"/\*{2}\s*([^*]*\*(?:[^/*][^*]*\*+)*)/\s*#define\s+((?:[^/]*?/?)+)\s*?(?:/{2,3}[^<].*?)?$", text, re.MULTILINE)
236 for comment, define in block_tmp:
237 block.append((" ".join(re.findall(r"^\s*\*?\s*(.*?)\s*?(?:/{2}.*?)?$", comment, re.MULTILINE)).strip(), define))
238 block += re.findall(r"/{3}\s*([^<].*?)\s*#define\s+((?:[^/]*?/?)+)\s*?(?:/{2,3}[^<].*?)?$", text, re.MULTILINE)
239 block += [(comment, define) for define, comment in re.findall(r"#define\s*(.*?)\s*/{3}<\s*(.+?)\s*?(?:/{2,3}[^<].*?)?$", text, re.MULTILINE)]
242 def formatParamNameValue(text):
244 Take the given string and return a tuple with the name of the parameter in the first position
245 and the value in the second.
247 block = re.findall("\s*([^\s]+)\s*(.+?)\s*$", text, re.MULTILINE)
250 def getDescriptionInformations(text):
252 Take the doxygen comment and strip the wizard informations, returning the tuple
253 (comment, wizard_informations)
255 index = text.find("$WIZARD")
257 exec(text[index + 1:])
258 informations = WIZARD
259 return text[:index].strip(), informations
261 return text.strip(), {}
263 def loadConfigurationInfos(path):
265 Return the module configurations found in the given file as a dict with the
266 parameter name as key and a dict containig the fields above as value:
267 "value": the value of the parameter
268 "description": the description of the parameter
269 "informations": a dict containig optional informations:
270 "type": "int" | "boolean" | "enum"
271 "min": the minimum value for integer parameters
272 "max": the maximum value for integer parameters
273 "long": boolean indicating if the num is a long
274 "value_list": the name of the enum for enum parameters
277 configurationInfos = {}
278 for comment, define in newParser.getDefinitionBlocks(open(path, "r").read()):
279 name, value = formatParamNameValue(define)
280 description, informations = newParser.getDescriptionInformations(comment)
281 configurationInfos[name] = {}
282 configurationInfos[name]["value"] = value
283 configurationInfos[name]["informations"] = informations
284 if ("type" in configurationInfos[name]["informations"].keys() and
285 configurationInfos[name]["informations"]["type"] == "int" and
286 configurationInfos[name]["value"].find("L") != -1):
287 configurationInfos[name]["informations"]["long"] = True
288 configurationInfos[name]["value"] = configurationInfos[name]["value"].replace("L", "")
289 if ("type" in configurationInfos[name]["informations"].keys() and
290 configurationInfos[name]["informations"]["type"] == "int" and
291 configurationInfos[name]["value"].find("U") != -1):
292 configurationInfos[name]["informations"]["unsigned"] = True
293 configurationInfos[name]["value"] = configurationInfos[name]["value"].replace("U", "")
294 configurationInfos[name]["description"] = description
295 return configurationInfos
296 except newParser.ParseError, err:
297 print "error in file %s. line: %d - statement %s" % (path, err.line_number, err.line)
302 def loadConfigurationInfos_old(path):
304 Return the module configurations found in the given file as a dict with the
305 parameter name as key and a dict containig the fields above as value:
306 "value": the value of the parameter
307 "description": the description of the parameter
308 "informations": a dict containig optional informations:
309 "type": "int" | "boolean" | "enum"
310 "min": the minimum value for integer parameters
311 "max": the maximum value for integer parameters
312 "long": boolean indicating if the num is a long
313 "value_list": the name of the enum for enum parameters
316 configurationInfos = {}
317 for comment, define in getDefinitionBlocks(open(path, "r").read()):
318 name, value = formatParamNameValue(define)
319 description, informations = getDescriptionInformations(comment)
320 configurationInfos[name] = {}
321 configurationInfos[name]["value"] = value
322 configurationInfos[name]["informations"] = informations
323 if ("type" in configurationInfos[name]["informations"].keys() and
324 configurationInfos[name]["informations"]["type"] == "int" and
325 configurationInfos[name]["value"].find("L") != -1):
326 configurationInfos[name]["informations"]["long"] = True
327 configurationInfos[name]["value"] = configurationInfos[name]["value"].replace("L", "")
328 if ("type" in configurationInfos[name]["informations"].keys() and
329 configurationInfos[name]["informations"]["type"] == "int" and
330 configurationInfos[name]["value"].find("U") != -1):
331 configurationInfos[name]["informations"]["unsigned"] = True
332 configurationInfos[name]["value"] = configurationInfos[name]["value"].replace("U", "")
333 configurationInfos[name]["description"] = description
334 return configurationInfos
336 raise DefineException.ConfigurationDefineException(path, name)
338 def loadDefineLists(path):
340 Return a dict with the name of the list as key and a list of string as value
343 string = open(path, "r").read()
344 commentList = re.findall(r"/\*{2}\s*([^*]*\*(?:[^/*][^*]*\*+)*)/", string)
345 commentList = [" ".join(re.findall(r"^\s*\*?\s*(.*?)\s*?(?:/{2}.*?)?$", comment, re.MULTILINE)).strip() for comment in commentList]
347 for comment in commentList:
348 index = comment.find("$WIZARD_LIST")
350 exec(comment[index + 1:])
351 listDict.update(WIZARD_LIST)
354 raise DefineException.EnumDefineException(path)
356 def loadModuleInfos(path):
358 Returns the module infos and the lists infos founded in the file located in the path,
359 and the configurations infos for the module defined in this file.
364 configurationsInfos = {}
365 string = open(path, "r").read()
366 commentList = re.findall(r"/\*{2}\s*([^*]*\*(?:[^/*][^*]*\*+)*)/", string)
367 commentList = [" ".join(re.findall(r"^\s*\*?\s*(.*?)\s*?(?:/{2}.*?)?$", comment, re.MULTILINE)).strip() for comment in commentList]
368 if len(commentList) > 0:
369 comment = commentList[0]
370 if comment.find("$WIZARD_MODULE") != -1:
371 index = comment.find("$WIZARD_MODULE")
373 # 14 is the length of "$WIZARD_MODULE"
374 if len(comment[index + 14:].strip()) > 0:
375 exec(comment[index + 1:])
376 moduleInfos[WIZARD_MODULE["name"]] = {"depends": WIZARD_MODULE["depends"],
377 "configuration": WIZARD_MODULE["configuration"],
380 index = comment.find("\\brief")
382 description = comment[index + 7:]
383 description = description[:description.find(" * ")]
384 moduleInfos[WIZARD_MODULE["name"]]["description"] = description
385 if "configuration" in WIZARD_MODULE.keys() and len(WIZARD_MODULE["configuration"]) > 0:
386 configurationsInfos[WIZARD_MODULE["configuration"]] = {}
387 listInfos.update(loadDefineLists(path))
388 return moduleInfos, listInfos, configurationsInfos
390 raise DefineException.ModuleDefineException(path)
392 def sub(string, parameter, value):
394 Substitute the given value at the given parameter define in the given string
396 return re.sub(r"(?P<define>#define\s+" + parameter + r"\s+)([^\s]+)", r"\g<define>" + value, string)
398 def isInt(informations):
400 Return True if the value is a simple int.
402 if ("long" not in informatios.keys() or not informations["long"]) and ("unsigned" not in informations.keys() or informations["unsigned"]):
407 def isLong(informations):
409 Return True if the value is a long.
411 if "long" in informations.keys() and informations["long"] and "unsigned" not in informations.keys():
416 def isUnsigned(informations):
418 Return True if the value is an unsigned.
420 if "unsigned" in informations.keys() and informations["unsigned"] and "long" not in informations.keys():
425 def isUnsignedLong(informations):
427 Return True if the value is an unsigned long.
429 if "unsigned" in informations.keys() and "long" in informations.keys() and informations["unsigned"] and informations["long"]: