4 # Copyright 2008 Develer S.r.l. (http://www.develer.com/)
9 # Author: Lorenzo Berni <duplo@develer.com>
19 import DefineException
21 def isBertosDir(directory):
22 return os.path.exists(directory + "/VERSION")
24 def bertosVersion(directory):
25 return open(directory + "/VERSION").readline().strip()
27 def createBertosProject(projectInfos):
28 directory = projectInfos.info("PROJECT_PATH")
29 sourcesDir = projectInfos.info("SOURCES_PATH")
30 if not os.path.isdir(directory):
32 f = open(directory + "/project.bertos", "w")
33 f.write(repr(projectInfos))
35 ## Destination source dir
36 srcdir = directory + "/bertos"
37 shutil.rmtree(srcdir, True)
38 shutil.copytree(sourcesDir + "/bertos", srcdir)
39 ## Destination makefile
40 makefile = directory + "/Makefile"
41 if os.path.exists(makefile):
43 makefile = open("mktemplates/Makefile").read()
44 makefile = makefileGenerator(projectInfos, makefile)
45 open(directory + "/Makefile", "w").write(makefile)
46 ## Destination project dir
47 prjdir = directory + "/" + os.path.basename(directory)
48 shutil.rmtree(prjdir, True)
50 ## Destination configurations files
51 cfgdir = prjdir + "/cfg"
52 shutil.rmtree(cfgdir, True)
54 for key, value in projectInfos.info("CONFIGURATIONS").items():
55 string = open(sourcesDir + "/" + key, "r").read()
56 for parameter, infos in value.items():
57 value = infos["value"]
58 if "unsigned" in infos["informations"].keys() and infos["informations"]["unsigned"]:
60 if "long" in infos["informations"].keys() and infos["informations"]["long"]:
62 string = sub(string, parameter, value)
63 f = open(cfgdir + "/" + os.path.basename(key), "w")
67 makefile = open("mktemplates/template.mk", "r").read()
68 makefile = mkGenerator(projectInfos, makefile)
69 open(prjdir + "/" + os.path.basename(prjdir) + ".mk", "w").write(makefile)
71 def mkGenerator(projectInfos, makefile):
73 Generates the mk file for the current project.
76 mkData["pname"] = os.path.basename(projectInfos.info("PROJECT_PATH"))
77 mkData["cpuname"] = projectInfos.info("CPU_INFOS")["CPU_NAME"]
78 mkData["cflags"] = " ".join(projectInfos.info("CPU_INFOS")["C_FLAGS"])
79 mkData["ldflags"] = " ".join(projectInfos.info("CPU_INFOS")["LD_FLAGS"])
81 while makefile.find(key) != -1:
82 makefile = makefile.replace(key, mkData[key])
85 def makefileGenerator(projectInfos, makefile):
87 Generate the Makefile for the current project.
89 # TODO: write a general function that works for both the mk file and the Makefile
90 while makefile.find("project_name") != -1:
91 makefile = makefile.replace("project_name", os.path.basename(projectInfos.info("PROJECT_PATH")))
95 path = os.environ["PATH"]
97 path = path.split(";")
99 path = path.split(":")
102 def findToolchains(pathList):
104 for element in pathList:
105 for toolchain in glob.glob(element+ "/" + const.GCC_NAME):
106 if not os.path.islink(toolchain):
107 toolchains.append(toolchain)
108 return list(set(toolchains))
110 def getToolchainInfo(output):
112 expr = re.compile("Target: .*")
113 target = expr.findall(output)
115 info["target"] = target[0].split("Target: ")[1]
116 expr = re.compile("gcc version [0-9,.]*")
117 version = expr.findall(output)
118 if len(version) == 1:
119 info["version"] = version[0].split("gcc version ")[1]
120 expr = re.compile("gcc version [0-9,.]* \(.*\)")
121 build = expr.findall(output)
123 build = build[0].split("gcc version ")[1]
124 build = build[build.find("(") + 1 : build.find(")")]
125 info["build"] = build
126 expr = re.compile("Configured with: .*")
127 configured = expr.findall(output)
128 if len(configured) == 1:
129 info["configured"] = configured[0].split("Configured with: ")[1]
130 expr = re.compile("Thread model: .*")
131 thread = expr.findall(output)
133 info["thread"] = thread[0].split("Thread model: ")[1]
136 def loadSourceTree(project):
137 fileList = [f for f in os.walk(project.info("SOURCES_PATH"))]
138 project.setInfo("FILE_LIST", fileList)
140 def findDefinitions(ftype, project):
141 L = project.info("FILE_LIST")
144 for filename in element[2]:
145 if fnmatch.fnmatch(filename, ftype):
146 definitions.append((filename, element[0]))
149 def loadCpuInfos(project):
151 for definition in findDefinitions(const.CPU_DEFINITION, project):
152 cpuInfos.append(getInfos(definition))
155 def getInfos(definition):
157 D.update(const.CPU_DEF)
158 def include(filename, dict = D, directory=definition[1]):
159 execfile(directory + "/" + filename, {}, D)
160 D["include"] = include
161 include(definition[0], D)
162 D["CPU_NAME"] = definition[0].split(".")[0]
163 D["DEFINITION_PATH"] = definition[1] + "/" + definition[0]
167 def getCommentList(string):
168 commentList = re.findall(r"/\*{2}\s*([^*]*\*(?:[^/*][^*]*\*+)*)/", string)
169 commentList = [re.findall(r"^\s*\* *(.*?)$", comment, re.MULTILINE) for comment in commentList]
172 def loadModuleDefinition(first_comment):
174 moduleDefinition = {}
175 for num, line in enumerate(first_comment):
176 index = line.find("$WIZ$")
180 exec line[index + len("$WIZ$ "):] in {}, moduleDefinition
182 raise ParseError(num, line[index:])
183 elif line.find("\\brief") != -1:
184 moduleDefinition["module_description"] = line[line.find("\\brief") + len("\\brief "):]
186 if "module_name" in moduleDefinition.keys():
187 moduleDict[moduleDefinition["module_name"]] = {}
188 if "module_depends" in moduleDefinition.keys():
189 if type(moduleDefinition["module_depends"]) == str:
190 moduleDefinition["module_depends"] = (moduleDefinition["module_depends"],)
191 moduleDict[moduleDefinition["module_name"]]["depends"] = moduleDefinition["module_depends"]
193 moduleDict[moduleDefinition["module_name"]]["depends"] = ()
194 if "module_configuration" in moduleDefinition.keys():
195 moduleDict[moduleDefinition["module_name"]]["configuration"] = moduleDefinition["module_configuration"]
197 moduleDict[moduleDefinition["module_name"]]["configuration"] = ""
198 if "module_description" in moduleDefinition.keys():
199 moduleDict[moduleDefinition["module_name"]]["description"] = moduleDefinition["module_description"]
200 moduleDict[moduleDefinition["module_name"]]["enabled"] = False
201 return toBeParsed, moduleDict
203 def loadDefineLists(commentList):
205 for comment in commentList:
206 for num, line in enumerate(comment):
207 index = line.find("$WIZ$")
210 exec line[index + len("$WIZ$ "):] in {}, defineList
212 raise ParseError(num, line[index:])
213 for key, value in defineList.items():
214 if type(value) == str:
215 defineList[key] = (value,)
218 def getDescriptionInformations(comment):
220 Take the doxygen comment and strip the wizard informations, returning the tuple
221 (comment, wizard_information)
225 for num, line in enumerate(comment):
226 index = line.find("$WIZ$")
228 description += " " + line[:index]
230 exec line[index + len("$WIZ$ "):] in {}, information
232 raise ParseError(num, line[index:])
234 description += " " + line
235 return description.strip(), information
237 def getDefinitionBlocks(text):
239 Take a text and return a list of tuple (description, name-value).
242 block_tmp = re.findall(r"/\*{2}\s*([^*]*\*(?:[^/*][^*]*\*+)*)/\s*#define\s+((?:[^/]*?/?)+)\s*?(?:/{2,3}[^<].*?)?$", text, re.MULTILINE)
243 for comment, define in block_tmp:
244 # Only the first element is needed
245 block.append(([re.findall(r"^\s*\* *(.*?)$", line, re.MULTILINE)[0] for line in comment.splitlines()], define))
246 for comment, define in re.findall(r"/{3}\s*([^<].*?)\s*#define\s+((?:[^/]*?/?)+)\s*?(?:/{2,3}[^<].*?)?$", text, re.MULTILINE):
247 block.append(([comment], define))
248 for define, comment in re.findall(r"#define\s*(.*?)\s*/{3}<\s*(.+?)\s*?(?:/{2,3}[^<].*?)?$", text, re.MULTILINE):
249 block.append(([comment], define))
252 def loadModuleData(project):
255 configurationInfoDict = {}
256 for filename, path in findDefinitions("*.h", project):
257 commentList = getCommentList(open(path + "/" + filename, "r").read())
258 if len(commentList) > 0:
260 configurationInfo = {}
262 toBeParsed, moduleDict = loadModuleDefinition(commentList[0])
263 except ParseError, err:
264 print "error in file %s. line: %d - statement %s" % (path + "/" + filename, err.line_number, err.line)
268 for module, information in moduleDict.items():
269 if "configuration" in information.keys() and len(information["configuration"]):
270 configuration = moduleDict[module]["configuration"]
271 configurationInfo[configuration] = loadConfigurationInfos(project.info("SOURCES_PATH") + "/" + configuration)
272 moduleInfoDict.update(moduleDict)
273 configurationInfoDict.update(configurationInfo)
276 listDict = loadDefineLists(commentList[1:])
277 listInfoDict.update(listDict)
278 except ParseError, err:
279 print "error in file %s. line: %d - statement %s" % (path + "/" + filename, err.line_number, err.line)
283 for filename, path in findDefinitions("*_" + project.info("CPU_INFOS")["TOOLCHAIN"] + ".h", project):
284 commentList = getCommentList(open(path + "/" + filename, "r").read())
285 listInfoDict.update(loadDefineLists(commentList))
286 project.setInfo("MODULES", moduleInfoDict)
287 project.setInfo("LISTS", listInfoDict)
288 project.setInfo("CONFIGURATIONS", configurationInfoDict)
290 def formatParamNameValue(text):
292 Take the given string and return a tuple with the name of the parameter in the first position
293 and the value in the second.
295 block = re.findall("\s*([^\s]+)\s*(.+?)\s*$", text, re.MULTILINE)
298 def loadConfigurationInfos(path):
300 Return the module configurations found in the given file as a dict with the
301 parameter name as key and a dict containig the fields above as value:
302 "value": the value of the parameter
303 "description": the description of the parameter
304 "informations": a dict containig optional informations:
305 "type": "int" | "boolean" | "enum"
306 "min": the minimum value for integer parameters
307 "max": the maximum value for integer parameters
308 "long": boolean indicating if the num is a long
309 "value_list": the name of the enum for enum parameters
312 configurationInfos = {}
313 for comment, define in getDefinitionBlocks(open(path, "r").read()):
314 name, value = formatParamNameValue(define)
315 description, informations = getDescriptionInformations(comment)
316 configurationInfos[name] = {}
317 configurationInfos[name]["value"] = value
318 configurationInfos[name]["informations"] = informations
319 if ("type" in configurationInfos[name]["informations"].keys() and
320 configurationInfos[name]["informations"]["type"] == "int" and
321 configurationInfos[name]["value"].find("L") != -1):
322 configurationInfos[name]["informations"]["long"] = True
323 configurationInfos[name]["value"] = configurationInfos[name]["value"].replace("L", "")
324 if ("type" in configurationInfos[name]["informations"].keys() and
325 configurationInfos[name]["informations"]["type"] == "int" and
326 configurationInfos[name]["value"].find("U") != -1):
327 configurationInfos[name]["informations"]["unsigned"] = True
328 configurationInfos[name]["value"] = configurationInfos[name]["value"].replace("U", "")
329 configurationInfos[name]["description"] = description
330 return configurationInfos
331 except ParseError, err:
332 print "error in file %s. line: %d - statement %s" % (path, err.line_number, err.line)
337 def sub(string, parameter, value):
339 Substitute the given value at the given parameter define in the given string
341 return re.sub(r"(?P<define>#define\s+" + parameter + r"\s+)([^\s]+)", r"\g<define>" + value, string)
343 def isInt(informations):
345 Return True if the value is a simple int.
347 if ("long" not in informatios.keys() or not informations["long"]) and ("unsigned" not in informations.keys() or informations["unsigned"]):
352 def isLong(informations):
354 Return True if the value is a long.
356 if "long" in informations.keys() and informations["long"] and "unsigned" not in informations.keys():
361 def isUnsigned(informations):
363 Return True if the value is an unsigned.
365 if "unsigned" in informations.keys() and informations["unsigned"] and "long" not in informations.keys():
370 def isUnsignedLong(informations):
372 Return True if the value is an unsigned long.
374 if "unsigned" in informations.keys() and "long" in informations.keys() and informations["unsigned"] and informations["long"]:
379 class ParseError(Exception):
380 def __init__(self, line_number, line):
381 Exception.__init__(self)
382 self.line_number = line_number