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 loadModuleData(project):
169 Loads all the module data, like module definition, list definition, and module configurations
170 int the given BProject, using the SOURCES_PATH information from this as the base for find the
175 configurationsInfoDict = {}
176 for filename, path in findDefinitions("*.h", project):
177 moduleInfos, listInfos, configurationInfos= loadModuleInfos(path + "/" + filename)
178 moduleInfosDict.update(moduleInfos)
179 listInfosDict.update(listInfos)
180 for configuration in configurationInfos.keys():
181 configurationsInfoDict[configuration] = loadConfigurationInfos(project.info("SOURCES_PATH") + "/" + configuration)
182 print "*_" + project.info("CPU_INFOS")["TOOLCHAIN"] + ".h"
183 for filename, path in findDefinitions("*_" + project.info("CPU_INFOS")["TOOLCHAIN"] + ".h", project):
184 listInfosDict.update(loadDefineLists(path + "/" + filename))
185 project.setInfo("MODULES", moduleInfosDict)
186 project.setInfo("LISTS", listInfosDict)
187 project.setInfo("CONFIGURATIONS", configurationsInfoDict)
189 def getDefinitionBlocks(text):
191 Take a text and return a list of tuple (description, name-value).
194 block_tmp = re.findall(r"/\*{2}\s*([^*]*\*(?:[^/*][^*]*\*+)*)/\s*#define\s+((?:[^/]*?/?)+)\s*?(?:/{2,3}[^<].*?)?$", text, re.MULTILINE)
195 for comment, define in block_tmp:
196 block.append((" ".join(re.findall(r"^\s*\*?\s*(.*?)\s*?(?:/{2}.*?)?$", comment, re.MULTILINE)).strip(), define))
197 block += re.findall(r"/{3}\s*([^<].*?)\s*#define\s+((?:[^/]*?/?)+)\s*?(?:/{2,3}[^<].*?)?$", text, re.MULTILINE)
198 block += [(comment, define) for define, comment in re.findall(r"#define\s*(.*?)\s*/{3}<\s*(.+?)\s*?(?:/{2,3}[^<].*?)?$", text, re.MULTILINE)]
201 def formatParamNameValue(text):
203 Take the given string and return a tuple with the name of the parameter in the first position
204 and the value in the second.
206 block = re.findall("\s*([^\s]+)\s*(.+?)\s*$", text, re.MULTILINE)
209 def getDescriptionInformations(text):
211 Take the doxygen comment and strip the wizard informations, returning the tuple
212 (comment, wizard_informations)
214 index = text.find("$WIZARD")
216 exec(text[index + 1:])
217 informations = WIZARD
218 return text[:index].strip(), informations
220 return text.strip(), {}
222 def loadConfigurationInfos(path):
224 Return the module configurations found in the given file as a dict with the
225 parameter name as key and a dict containig the fields above as value:
226 "value": the value of the parameter
227 "description": the description of the parameter
228 "informations": a dict containig optional informations:
229 "type": "int" | "boolean" | "enum"
230 "min": the minimum value for integer parameters
231 "max": the maximum value for integer parameters
232 "long": boolean indicating if the num is a long
233 "value_list": the name of the enum for enum parameters
236 configurationInfos = {}
237 for comment, define in getDefinitionBlocks(open(path, "r").read()):
238 name, value = formatParamNameValue(define)
239 description, informations = getDescriptionInformations(comment)
240 configurationInfos[name] = {}
241 configurationInfos[name]["value"] = value
242 configurationInfos[name]["informations"] = informations
243 if ("type" in configurationInfos[name]["informations"].keys() and
244 configurationInfos[name]["informations"]["type"] == "int" and
245 configurationInfos[name]["value"].find("L") != -1):
246 configurationInfos[name]["informations"]["long"] = True
247 configurationInfos[name]["value"] = configurationInfos[name]["value"].replace("L", "")
248 if ("type" in configurationInfos[name]["informations"].keys() and
249 configurationInfos[name]["informations"]["type"] == "int" and
250 configurationInfos[name]["value"].find("U") != -1):
251 configurationInfos[name]["informations"]["unsigned"] = True
252 configurationInfos[name]["value"] = configurationInfos[name]["value"].replace("U", "")
253 configurationInfos[name]["description"] = description
254 return configurationInfos
256 raise DefineException.ConfigurationDefineException(path, name)
258 def loadDefineLists(path):
260 Return a dict with the name of the list as key and a list of string as value
263 string = open(path, "r").read()
264 commentList = re.findall(r"/\*{2}\s*([^*]*\*(?:[^/*][^*]*\*+)*)/", string)
265 commentList = [" ".join(re.findall(r"^\s*\*?\s*(.*?)\s*?(?:/{2}.*?)?$", comment, re.MULTILINE)).strip() for comment in commentList]
267 for comment in commentList:
268 index = comment.find("$WIZARD_LIST")
270 exec(comment[index + 1:])
271 listDict.update(WIZARD_LIST)
274 raise DefineException.EnumDefineException(path)
276 def loadModuleInfos(path):
278 Returns the module infos and the lists infos founded in the file located in the path,
279 and the configurations infos for the module defined in this file.
284 configurationsInfos = {}
285 string = open(path, "r").read()
286 commentList = re.findall(r"/\*{2}\s*([^*]*\*(?:[^/*][^*]*\*+)*)/", string)
287 commentList = [" ".join(re.findall(r"^\s*\*?\s*(.*?)\s*?(?:/{2}.*?)?$", comment, re.MULTILINE)).strip() for comment in commentList]
288 if len(commentList) > 0:
289 comment = commentList[0]
290 if comment.find("$WIZARD_MODULE") != -1:
291 index = comment.find("$WIZARD_MODULE")
293 # 14 is the length of "$WIZARD_MODULE"
294 if len(comment[index + 14:].strip()) > 0:
295 exec(comment[index + 1:])
296 moduleInfos[WIZARD_MODULE["name"]] = {"depends": WIZARD_MODULE["depends"],
297 "configuration": WIZARD_MODULE["configuration"],
300 index = comment.find("\\brief")
302 description = comment[index + 7:]
303 description = description[:description.find(" * ")]
304 moduleInfos[WIZARD_MODULE["name"]]["description"] = description
305 if "configuration" in WIZARD_MODULE.keys() and len(WIZARD_MODULE["configuration"]) > 0:
306 configurationsInfos[WIZARD_MODULE["configuration"]] = {}
307 listInfos.update(loadDefineLists(path))
308 return moduleInfos, listInfos, configurationsInfos
310 raise DefineException.ModuleDefineException(path)
312 def sub(string, parameter, value):
314 Substitute the given value at the given parameter define in the given string
316 return re.sub(r"(?P<define>#define\s+" + parameter + r"\s+)([^\s]+)", r"\g<define>" + value, string)
318 def isInt(informations):
320 Return True if the value is a simple int.
322 if ("long" not in informatios.keys() or not informations["long"]) and ("unsigned" not in informations.keys() or informations["unsigned"]):
327 def isLong(informations):
329 Return True if the value is a long.
331 if "long" in informations.keys() and informations["long"] and "unsigned" not in informations.keys():
336 def isUnsigned(informations):
338 Return True if the value is an unsigned.
340 if "unsigned" in informations.keys() and informations["unsigned"] and "long" not in informations.keys():
345 def isUnsignedLong(informations):
347 Return True if the value is an unsigned long.
349 if "unsigned" in informations.keys() and "long" in informations.keys() and informations["unsigned"] and informations["long"]: