Add setEnabledModules as method of BProject class (instead of function).
[bertos.git] / wizard / bertos_utils.py
1 #!/usr/bin/env python
2 # encoding: utf-8
3 #
4 # This file is part of BeRTOS.
5 #
6 # Bertos is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19 #
20 # As a special exception, you may use this file as part of a free software
21 # library without restriction.  Specifically, if other files instantiate
22 # templates or use macros or inline functions from this file, or you compile
23 # this file and link it with other files to produce an executable, this
24 # file does not by itself cause the resulting executable to be covered by
25 # the GNU General Public License.  This exception does not however
26 # invalidate any other reasons why the executable file might be covered by
27 # the GNU General Public License.
28 #
29 # Copyright 2008 Develer S.r.l. (http://www.develer.com/)
30 #
31 # $Id$
32 #
33 # Author: Lorenzo Berni <duplo@develer.com>
34 #
35
36 import os
37 import fnmatch
38 import glob
39 import re
40 import shutil
41 # Use custom copytree function
42 import copytree
43 import pickle
44
45 import const
46 import plugins
47 import DefineException
48
49 from _wizard_version import WIZARD_VERSION
50
51 from LoadException import VersionException, ToolchainException
52
53 def isBertosDir(directory):
54    return os.path.exists(directory + "/VERSION")
55
56 def bertosVersion(directory):
57    return open(directory + "/VERSION").readline().strip()
58
59 def enabledModules(project_info):
60     enabled_modules = []
61     for name, module in project_info.info("MODULES").items():
62         if module["enabled"]:
63             enabled_modules.append(name)
64     return enabled_modules
65
66 def presetList(directory):
67     """
68     Return the list of the preset found in the selected BeRTOS Version.
69     """
70     abspath = os.path.join(directory, const.PREDEFINED_BOARDS_DIR)
71     preset_list = dict([
72         (os.path.join(abspath, preset_dir), presetInfo(os.path.join(abspath, preset_dir)))
73         for preset_dir in os.listdir(os.path.join(directory, const.PREDEFINED_BOARDS_DIR))
74     ])
75     return preset_list
76
77 def presetInfo(preset_dir):
78     """
79     Return the preset-relevant info contined into the project_file.
80     """
81     preset_info = pickle.loads(open(os.path.join(preset_dir, "project.bertos"), "r").read())
82     try:
83         description = open(os.path.join(preset_dir, "description"), "r").read()
84     except IOError:
85         # No description file found.
86         description = ""
87     relevant_info = {
88         "CPU_NAME": preset_info.get("CPU_NAME"),
89         "SELECTED_FREQ": preset_info.get("SELECTED_FREQ"),
90         "WIZARD_VERSION": preset_info.get("WIZARD_VERSION", None),
91         "PRESET_NAME": preset_info.get("PROJECT_NAME"),
92         "PRESET_DESCRIPTION": description.decode("utf-8"),
93     }
94     return relevant_info
95
96 def mergeSources(srcdir, new_sources, old_sources):
97     # The current mergeSources function provide only a raw copy of the sources in the
98     # created project.
99     #
100     # TODO: implement the three way merge algorithm
101     #
102     shutil.rmtree(srcdir, True)
103     copytree.copytree(os.path.join(new_sources, "bertos"), srcdir, ignore_list=const.IGNORE_LIST)
104
105 def projectFileGenerator(project_info):
106     directory = project_info.info("PROJECT_PATH")
107     project_data = {}
108     enabled_modules = []
109     for module, information in project_info.info("MODULES").items():
110         if information["enabled"]:
111             enabled_modules.append(module)
112     project_data["ENABLED_MODULES"] = enabled_modules
113     # Use the local BeRTOS version instead of the original one
114     # project_data["SOURCES_PATH"] = project_info.info("SOURCES_PATH")
115     project_data["SOURCES_PATH"] = directory
116     project_data["PROJECT_NAME"] = project_info.info("PROJECT_NAME", os.path.basename(directory))
117     project_data["TOOLCHAIN"] = project_info.info("TOOLCHAIN")
118     project_data["CPU_NAME"] = project_info.info("CPU_NAME")
119     project_data["SELECTED_FREQ"] = project_info.info("SELECTED_FREQ")
120     project_data["OUTPUT"] = project_info.info("OUTPUT")
121     project_data["WIZARD_VERSION"] = WIZARD_VERSION
122     return pickle.dumps(project_data)
123
124 def createBertosProject(project_info, edit=False):
125     directory = project_info.info("PROJECT_PATH")
126     sources_dir = project_info.info("SOURCES_PATH")
127     old_sources_dir = project_info.info("OLD_SOURCES_PATH")
128     if not edit:
129         if os.path.isdir(directory):
130             shutil.rmtree(directory, True)        
131         os.makedirs(directory)
132     # Write the project file
133     f = open(directory + "/project.bertos", "w")
134     f.write(projectFileGenerator(project_info))
135     f.close()
136     # VERSION file
137     version_file = open(os.path.join(const.DATA_DIR, "vtemplates/VERSION"), "r").read()
138     open(directory + "/VERSION", "w").write(versionFileGenerator(project_info, version_file))
139     # Destination source dir
140     srcdir = directory + "/bertos"
141     if not edit:
142         # If not in editing mode it copies all the bertos sources in the /bertos subdirectory of the project
143         shutil.rmtree(srcdir, True)
144         copytree.copytree(sources_dir + "/bertos", srcdir, ignore_list=const.IGNORE_LIST)
145     elif old_sources_dir:
146         # If in editing mode it merges the current bertos sources with the selected ones
147         # TODO: implement the three way merge algotihm
148         #
149         mergeSources(srcdir, sources_dir, old_sources_dir)
150     # Destination makefile
151     makefile = directory + "/Makefile"
152     makefile = open(os.path.join(const.DATA_DIR, "mktemplates/Makefile"), 'r').read()
153     makefile = makefileGenerator(project_info, makefile)
154     open(directory + "/Makefile", "w").write(makefile)
155     # Destination project dir
156     # prjdir = directory + "/" + os.path.basename(directory)
157     prjdir = os.path.join(directory, project_info.info("PROJECT_NAME"))
158     if not edit:
159         shutil.rmtree(prjdir, True)
160         os.mkdir(prjdir)
161     # Destination hw files
162     hwdir = prjdir + "/hw"
163     if not edit:
164         shutil.rmtree(hwdir, True)
165         os.mkdir(hwdir)
166     # Copy all the hw files
167     for module, information in project_info.info("MODULES").items():
168         for hwfile in information["hw"]:
169             string = open(sources_dir + "/" + hwfile, "r").read()
170             hwfile_path = hwdir + "/" + os.path.basename(hwfile)
171             if not edit or not os.path.exists(hwfile_path):
172                 # If not in editing mode it copies all the hw files. If in
173                 # editing mode it copies only the files that don't exist yet
174                 open(hwdir + "/" + os.path.basename(hwfile), "w").write(string)
175     # Destination configurations files
176     cfgdir = prjdir + "/cfg"
177     if not edit:
178         shutil.rmtree(cfgdir, True)
179         os.mkdir(cfgdir)
180     # Set properly the autoenabled parameters
181     for module, information in project_info.info("MODULES").items():
182         if "configuration" in information and information["configuration"] != "":
183             configurations = project_info.info("CONFIGURATIONS")
184             configuration = configurations[information["configuration"]]
185             for start, parameter in configuration["paramlist"]:
186                 if "type" in configuration[parameter]["informations"] and configuration[parameter]["informations"]["type"] == "autoenabled":
187                     configuration[parameter]["value"] = "1" if information["enabled"] else "0"
188             project_info.setInfo("CONFIGURATIONS", configurations)
189     # Copy all the configuration files
190     for configuration, information in project_info.info("CONFIGURATIONS").items():
191         string = open(sources_dir + "/" + configuration, "r").read()
192         for start, parameter in information["paramlist"]:
193             infos = information[parameter]
194             value = infos["value"]
195             if "unsigned" in infos["informations"] and infos["informations"]["unsigned"]:
196                 value += "U"
197             if "long" in infos["informations"] and infos["informations"]["long"]:
198                 value += "L"
199             string = sub(string, parameter, value)
200         f = open(cfgdir + "/" + os.path.basename(configuration), "w")
201         f.write(string)
202         f.close()
203     if not edit:
204         # Destination user mk file (only on project creation)
205         makefile = open(os.path.join(const.DATA_DIR, "mktemplates/template.mk"), "r").read()
206         # Deadly performances loss was here :(
207         makefile = userMkGenerator(project_info, makefile)
208         open(prjdir + "/" + os.path.basename(prjdir) + ".mk", "w").write(makefile)
209     # Destination wizard mk file
210     makefile = open(os.path.join(const.DATA_DIR, "mktemplates/template_wiz.mk"), "r").read()
211     makefile = mkGenerator(project_info, makefile)
212     open(prjdir + "/" + os.path.basename(prjdir) + "_wiz.mk", "w").write(makefile)
213     # Destination main.c file
214     if not edit:
215         main = open(os.path.join(const.DATA_DIR, "srctemplates/main.c"), "r").read()
216         open(prjdir + "/main.c", "w").write(main)
217     # Files for selected plugins
218     relevants_files = {}
219     for plugin in project_info.info("OUTPUT"):
220         module = loadPlugin(plugin)
221         relevants_files[plugin] = module.createProject(project_info)
222     project_info.setInfo("RELEVANT_FILES", relevants_files)
223
224 def loadPlugin(plugin):
225     """
226     Returns the given plugin module.
227     """
228     return getattr(__import__("plugins", {}, {}, [plugin]), plugin)
229
230 def versionFileGenerator(project_info, version_file):
231     version = bertosVersion(project_info.info("SOURCES_PATH"))
232     return version_file.replace('$version', version)
233
234 def userMkGenerator(project_info, makefile):
235     mk_data = {}
236     mk_data["$pname"] = os.path.basename(project_info.info("PROJECT_PATH"))
237     mk_data["$main"] = os.path.basename(project_info.info("PROJECT_PATH")) + "/main.c"
238     for key in mk_data:
239         while makefile.find(key) != -1:
240             makefile = makefile.replace(key, mk_data[key])
241     return makefile
242
243 def mkGenerator(project_info, makefile):
244     """
245     Generates the mk file for the current project.
246     """
247     mk_data = {}
248     mk_data["$pname"] = os.path.basename(project_info.info("PROJECT_PATH"))
249     mk_data["$cpuclockfreq"] = project_info.info("SELECTED_FREQ")
250     cpu_mk_parameters = []
251     for key, value in project_info.info("CPU_INFOS").items():
252         if key.startswith(const.MK_PARAM_ID):
253             cpu_mk_parameters.append("%s = %s" %(key.replace("MK", mk_data["$pname"]), value))
254     mk_data["$cpuparameters"] = "\n".join(cpu_mk_parameters)
255     mk_data["$csrc"], mk_data["$pcsrc"], mk_data["$cppasrc"], mk_data["$cxxsrc"], mk_data["$asrc"], mk_data["$constants"] = csrcGenerator(project_info)
256     mk_data["$prefix"] = replaceSeparators(project_info.info("TOOLCHAIN")["path"].split("gcc")[0])
257     mk_data["$suffix"] = replaceSeparators(project_info.info("TOOLCHAIN")["path"].split("gcc")[1])
258     mk_data["$main"] = os.path.basename(project_info.info("PROJECT_PATH")) + "/main.c"
259     for key in mk_data:
260         while makefile.find(key) != -1:
261             makefile = makefile.replace(key, mk_data[key])
262     return makefile
263
264 def makefileGenerator(project_info, makefile):
265     """
266     Generate the Makefile for the current project.
267     """
268     # TODO write a general function that works for both the mk file and the Makefile
269     while makefile.find("$pname") != -1:
270         makefile = makefile.replace("$pname", os.path.basename(project_info.info("PROJECT_PATH")))
271     return makefile
272
273 def csrcGenerator(project_info):
274     modules = project_info.info("MODULES")
275     files = project_info.info("FILES")
276     if "harvard" in project_info.info("CPU_INFOS")["CPU_TAGS"]:
277         harvard = True
278     else:
279         harvard = False
280     # file to be included in CSRC variable
281     csrc = []
282     # file to be included in PCSRC variable
283     pcsrc = []
284     # files to be included in CPPASRC variable
285     cppasrc = []
286     # files to be included in CXXSRC variable
287     cxxsrc = []
288     # files to be included in ASRC variable
289     asrc = []
290     # constants to be included at the beginning of the makefile
291     constants = {}
292     for module, information in modules.items():
293         module_files = set([])
294         dependency_files = set([])
295         # assembly sources
296         asm_files = set([])
297         hwdir = os.path.basename(project_info.info("PROJECT_PATH")) + "/hw" 
298         if information["enabled"]:
299             if "constants" in information:
300                 constants.update(information["constants"])
301             cfiles, sfiles = findModuleFiles(module, project_info)
302             module_files |= set(cfiles)
303             asm_files |= set(sfiles)
304             for file in information["hw"]:
305                 if file.endswith(".c"):
306                     module_files |= set([hwdir + "/" + os.path.basename(file)])
307             for file_dependency in information["depends"] + tuple(files.keys()):
308                     dependencyCFiles, dependencySFiles = findModuleFiles(file_dependency, project_info)
309                     dependency_files |= set(dependencyCFiles)
310                     asm_files |= set(dependencySFiles)
311             for file in module_files:
312                 if not harvard or information.get("harvard", "both") == "both":
313                     csrc.append(file)
314                 if harvard and "harvard" in information:
315                     pcsrc.append(file)
316             for file in dependency_files:
317                 csrc.append(file)
318             for file in project_info.info("CPU_INFOS")["C_SRC"]:
319                 csrc.append(file)
320             for file in project_info.info("CPU_INFOS")["PC_SRC"]:
321                 pcsrc.append(file)
322             for file in asm_files:
323                 cppasrc.append(file)
324     for file in project_info.info("CPU_INFOS")["CPPA_SRC"]:
325         cppasrc.append(file)
326     for file in project_info.info("CPU_INFOS")["CXX_SRC"]:
327         cxxsrc.append(file)
328     for file in project_info.info("CPU_INFOS")["ASRC"]:
329         asrc.append(file)
330     csrc = set(csrc)
331     csrc = " \\\n\t".join(csrc) + " \\"
332     pcsrc = set(pcsrc)
333     pcsrc = " \\\n\t".join(pcsrc) + " \\"
334     cppasrc = set(cppasrc)
335     cppasrc = " \\\n\t".join(cppasrc) + " \\"
336     cxxsrc = set(cxxsrc)
337     cxxsrc = " \\\n\t".join(cxxsrc) + " \\"
338     asrc = set(asrc)
339     asrc = " \\\n\t".join(asrc) + " \\"
340     constants = "\n".join([os.path.basename(project_info.info("PROJECT_PATH")) + "_" + key + " = " + unicode(value) for key, value in constants.items()])
341     return csrc, pcsrc, cppasrc, cxxsrc, asrc, constants
342
343 def findModuleFiles(module, project_info):
344     # Find the files related to the selected module
345     cfiles = []
346     sfiles = []
347     # .c files related to the module and the cpu architecture
348     for filename, path in project_info.searchFiles(module + ".c"):
349         path = path.replace(project_info.info("SOURCES_PATH") + os.sep, "")
350         path = replaceSeparators(path)
351         cfiles.append(path + "/" + filename)
352     # .s files related to the module and the cpu architecture
353     for filename, path in project_info.searchFiles(module + ".s") + \
354             project_info.searchFiles(module + ".S"):
355         path = path.replace(project_info.info("SOURCES_PATH") + os.sep, "")
356         path = replaceSeparators(path)
357         sfiles.append(path + "/" + filename)
358     # .c and .s files related to the module and the cpu tags
359     tags = project_info.info("CPU_INFOS")["CPU_TAGS"]
360
361     # Awful, but secure check for version
362     # TODO: split me in a method/function
363     try:
364         version_string = bertosVersion(project_info.info("SOURCES_PATH"))
365         version_list = [int(i) for i in version_string.split()[-1].split('.')]
366         if version_list < [2, 5]:
367             # For older versions of BeRTOS add the toolchain to the tags
368             tags.append(project_info.info("CPU_INFOS")["TOOLCHAIN"])
369     except ValueError:
370         # If the version file hasn't a valid version number do nothing
371         pass
372
373     for tag in tags:
374         for filename, path in project_info.searchFiles(module + "_" + tag + ".c"):
375             path = path.replace(project_info.info("SOURCES_PATH") + os.sep, "")
376             if os.sep != "/":
377                 path = replaceSeparators(path)
378             cfiles.append(path + "/" + filename)
379         for filename, path in project_info.searchFiles(module + "_" + tag + ".s") + \
380                 project_info.searchFiles(module + "_" + tag + ".S"):
381             path = path.replace(project_info.info("SOURCES_PATH") + os.sep, "")
382             path = replaceSeparators(path)
383             sfiles.append(path + "/" + filename)
384     return cfiles, sfiles
385
386 def replaceSeparators(path):
387     """
388     Replace the separators in the given path with unix standard separator.
389     """
390     if os.sep != "/":
391         while path.find(os.sep) != -1:
392             path = path.replace(os.sep, "/")
393     return path
394
395 def getSystemPath():
396     path = os.environ["PATH"]
397     if os.name == "nt":
398         path = path.split(";")
399     else:
400         path = path.split(":")
401     return path
402
403 def findToolchains(path_list):
404     toolchains = []
405     for element in path_list:
406         for toolchain in glob.glob(element+ "/" + const.GCC_NAME):
407             toolchains.append(toolchain)
408     return list(set(toolchains))
409
410 def getToolchainInfo(output):
411     info = {}
412     expr = re.compile("Target: .*")
413     target = expr.findall(output)
414     if len(target) == 1:
415         info["target"] = target[0].split("Target: ")[1]
416     expr = re.compile("gcc version [0-9,.]*")
417     version = expr.findall(output)
418     if len(version) == 1:
419         info["version"] = version[0].split("gcc version ")[1]
420     expr = re.compile("gcc version [0-9,.]* \(.*\)")
421     build = expr.findall(output)
422     if len(build) == 1:
423         build = build[0].split("gcc version ")[1]
424         build = build[build.find("(") + 1 : build.find(")")]
425         info["build"] = build
426     expr = re.compile("Configured with: .*")
427     configured = expr.findall(output)
428     if len(configured) == 1:
429         info["configured"] = configured[0].split("Configured with: ")[1]
430     expr = re.compile("Thread model: .*")
431     thread = expr.findall(output)
432     if len(thread) == 1:
433         info["thread"] = thread[0].split("Thread model: ")[1]
434     return info
435
436 def getToolchainName(toolchain_info):
437     name = "GCC " + toolchain_info["version"] + " - " + toolchain_info["target"].strip()
438     return name
439
440 def getTagSet(cpu_info):
441     tag_set = set([])
442     for cpu in cpu_info:
443         tag_set |= set([cpu["CPU_NAME"]])
444         tag_set |= set(cpu["CPU_TAGS"])
445         tag_set |= set([cpu["TOOLCHAIN"]])
446     return tag_set
447         
448
449 def getInfos(definition):
450     D = {}
451     D.update(const.CPU_DEF)
452     def include(filename, dict = D, directory=definition[1]):
453         execfile(directory + "/" + filename, {}, D)
454     D["include"] = include
455     include(definition[0], D)
456     D["CPU_NAME"] = definition[0].split(".")[0]
457     D["DEFINITION_PATH"] = definition[1] + "/" + definition[0]
458     del D["include"]
459     return D
460
461 def getCommentList(string):
462     comment_list = re.findall(r"/\*{2}\s*([^*]*\*(?:[^/*][^*]*\*+)*)/", string)
463     comment_list = [re.findall(r"^\s*\* *(.*?)$", comment, re.MULTILINE) for comment in comment_list]
464     return comment_list
465
466 def loadModuleDefinition(first_comment):
467     to_be_parsed = False
468     module_definition = {}
469     for num, line in enumerate(first_comment):
470         index = line.find("$WIZ$")
471         if index != -1:
472             to_be_parsed = True
473             try:
474                 exec line[index + len("$WIZ$ "):] in {}, module_definition
475             except:
476                 raise ParseError(num, line[index:])
477         elif line.find("\\brief") != -1:
478             module_definition["module_description"] = line[line.find("\\brief") + len("\\brief "):]
479     module_dict = {}
480     if "module_name" in module_definition:
481         module_name = module_definition[const.MODULE_DEFINITION["module_name"]]
482         del module_definition[const.MODULE_DEFINITION["module_name"]]
483         module_dict[module_name] = {}
484         if const.MODULE_DEFINITION["module_depends"] in module_definition:
485             depends = module_definition[const.MODULE_DEFINITION["module_depends"]]
486             del module_definition[const.MODULE_DEFINITION["module_depends"]]
487             if type(depends) == str:
488                 depends = (depends,)
489             module_dict[module_name]["depends"] = depends
490         else:
491             module_dict[module_name]["depends"] = ()
492         if const.MODULE_DEFINITION["module_configuration"] in module_definition:
493             module_dict[module_name]["configuration"] = module_definition[const.MODULE_DEFINITION["module_configuration"]]
494             del module_definition[const.MODULE_DEFINITION["module_configuration"]]
495         else:
496             module_dict[module_name]["configuration"] = ""
497         if "module_description" in module_definition:
498             module_dict[module_name]["description"] = module_definition["module_description"]
499             del module_definition["module_description"]
500         if const.MODULE_DEFINITION["module_harvard"] in module_definition:
501             harvard = module_definition[const.MODULE_DEFINITION["module_harvard"]]
502             module_dict[module_name]["harvard"] = harvard
503             del module_definition[const.MODULE_DEFINITION["module_harvard"]]
504         if const.MODULE_DEFINITION["module_hw"] in module_definition:
505             hw = module_definition[const.MODULE_DEFINITION["module_hw"]]
506             del module_definition[const.MODULE_DEFINITION["module_hw"]]
507             if type(hw) == str:
508                 hw = (hw, )
509             module_dict[module_name]["hw"] = hw
510         else:
511             module_dict[module_name]["hw"] = ()
512         if const.MODULE_DEFINITION["module_supports"] in module_definition:
513             supports = module_definition[const.MODULE_DEFINITION["module_supports"]]
514             del module_definition[const.MODULE_DEFINITION["module_supports"]]
515             module_dict[module_name]["supports"] = supports
516         module_dict[module_name]["constants"] = module_definition
517         module_dict[module_name]["enabled"] = False
518     return to_be_parsed, module_dict
519
520 def isSupported(project, module=None, property_id=None):
521     if not module and property_id:
522         item = project.info("CONFIGURATIONS")[property_id[0]][property_id[1]]["informations"]
523     else:
524         item = project.info("MODULES")[module]
525     tag_dict = project.info("ALL_CPU_TAGS")
526     if "supports" in item:
527         support_string = item["supports"]
528         supported = {}
529         try:
530             exec "supported = " + support_string in tag_dict, supported
531         except:
532             raise SupportedException(support_string)
533         return supported["supported"]
534     else:
535         return True
536
537 def loadDefineLists(comment_list):
538     define_list = {}
539     for comment in comment_list:
540         for num, line in enumerate(comment):
541             index = line.find("$WIZ$")
542             if index != -1:
543                 try:
544                     exec line[index + len("$WIZ$ "):] in {}, define_list
545                 except:
546                     raise ParseError(num, line[index:])
547     for key, value in define_list.items():
548         if type(value) == str:
549             define_list[key] = (value,)
550     return define_list
551
552 def getDescriptionInformations(comment):
553     """
554     Take the doxygen comment and strip the wizard informations, returning the tuple
555     (comment, wizard_information)
556     """
557     brief = ""
558     description = ""
559     information = {}
560     for num, line in enumerate(comment):
561         index = line.find("$WIZ$")
562         if index != -1:
563             if len(brief) == 0:
564                 brief += line[:index].strip()
565             else:
566                 description += " " + line[:index]
567             try:
568                 exec line[index + len("$WIZ$ "):] in {}, information
569             except:
570                 raise ParseError(num, line[index:])
571         else:
572             if len(brief) == 0:
573                 brief += line.strip()
574             else:
575                 description += " " + line
576                 description = description.strip()
577     return brief.strip(), description.strip(), information
578
579 def getDefinitionBlocks(text):
580     """
581     Take a text and return a list of tuple (description, name-value).
582     """
583     block = []
584     block_tmp = re.finditer(r"/\*{2}\s*([^*]*\*(?:[^/*][^*]*\*+)*)/\s*#define\s+((?:[^/]*?/?)+)\s*?(?:/{2,3}[^<].*?)?$", text, re.MULTILINE)
585     for match in block_tmp:
586         # Only the first element is needed
587         comment = match.group(1)
588         define = match.group(2)
589         start = match.start()
590         block.append(([re.findall(r"^\s*\* *(.*?)$", line, re.MULTILINE)[0] for line in comment.splitlines()], define, start))
591     for match in re.finditer(r"/{3}\s*([^<].*?)\s*#define\s+((?:[^/]*?/?)+)\s*?(?:/{2,3}[^<].*?)?$", text, re.MULTILINE):
592         comment = match.group(1)
593         define = match.group(2)
594         start = match.start()
595         block.append(([comment], define, start))
596     for match in re.finditer(r"#define\s*(.*?)\s*/{3}<\s*(.+?)\s*?(?:/{2,3}[^<].*?)?$", text, re.MULTILINE):
597         comment = match.group(2)
598         define = match.group(1)
599         start = match.start()
600         block.append(([comment], define, start))
601     return block
602
603 def formatParamNameValue(text):
604     """
605     Take the given string and return a tuple with the name of the parameter in the first position
606     and the value in the second.
607     """
608     block = re.findall("\s*([^\s]+)\s*(.+?)\s*$", text, re.MULTILINE)
609     return block[0]
610
611 def loadConfigurationInfos(path):
612     """
613     Return the module configurations found in the given file as a dict with the
614     parameter name as key and a dict containig the fields above as value:
615         "value": the value of the parameter
616         "description": the description of the parameter
617         "informations": a dict containig optional informations:
618             "type": "int" | "boolean" | "enum"
619             "min": the minimum value for integer parameters
620             "max": the maximum value for integer parameters
621             "long": boolean indicating if the num is a long
622             "unsigned": boolean indicating if the num is an unsigned
623             "value_list": the name of the enum for enum parameters
624             "conditional_deps": the list of conditional dependencies for boolean parameters
625     """
626     configuration_infos = {}
627     configuration_infos["paramlist"] = []
628     for comment, define, start in getDefinitionBlocks(open(path, "r").read()):
629         name, value = formatParamNameValue(define)
630         brief, description, informations = getDescriptionInformations(comment)
631         configuration_infos["paramlist"].append((start, name))
632         configuration_infos[name] = {}
633         configuration_infos[name]["value"] = value
634         configuration_infos[name]["informations"] = informations
635         if not "type" in configuration_infos[name]["informations"]:
636             configuration_infos[name]["informations"]["type"] = findParameterType(configuration_infos[name])
637         if ("type" in configuration_infos[name]["informations"] and
638                 configuration_infos[name]["informations"]["type"] == "int" and
639                 configuration_infos[name]["value"].find("L") != -1):
640             configuration_infos[name]["informations"]["long"] = True
641             configuration_infos[name]["value"] = configuration_infos[name]["value"].replace("L", "")
642         if ("type" in configuration_infos[name]["informations"] and
643                 configuration_infos[name]["informations"]["type"] == "int" and
644                 configuration_infos[name]["value"].find("U") != -1):
645             configuration_infos[name]["informations"]["unsigned"] = True
646             configuration_infos[name]["value"] = configuration_infos[name]["value"].replace("U", "")
647         if "conditional_deps" in configuration_infos[name]["informations"]:
648             if (type(configuration_infos[name]["informations"]["conditional_deps"]) == str or
649                     type(configuration_infos[name]["informations"]["conditional_deps"]) == unicode):
650                 configuration_infos[name]["informations"]["conditional_deps"] = (configuration_infos[name]["informations"]["conditional_deps"], )
651             elif type(configuration_infos[name]["informations"]["conditional_deps"]) == tuple:
652                 pass
653             else:
654                 configuration_infos[name]["informations"]["conditional_deps"] = ()
655         configuration_infos[name]["description"] = description
656         configuration_infos[name]["brief"] = brief
657     return configuration_infos
658
659 def updateConfigurationValues(def_conf, user_conf):
660     for param in def_conf["paramlist"]:
661         if param[1] in user_conf and "value" in user_conf[param[1]]:
662             def_conf[param[1]]["value"] = user_conf[param[1]]["value"]
663     return def_conf
664
665 def findParameterType(parameter):
666     if "value_list" in parameter["informations"]:
667         return "enum"
668     if "min" in parameter["informations"] or "max" in parameter["informations"] or re.match(r"^\d+U?L?$", parameter["value"]) != None:
669         return "int"
670
671 def sub(string, parameter, value):
672     """
673     Substitute the given value at the given parameter define in the given string
674     """
675     return re.sub(r"(?P<define>#define\s+" + parameter + r"\s+)([^\s]+)", r"\g<define>" + value, string)
676
677 def isInt(informations):
678     """
679     Return True if the value is a simple int.
680     """
681     if ("long" not in informatios or not informations["long"]) and ("unsigned" not in informations or informations["unsigned"]):
682         return True
683     else:
684         return False
685
686 def isLong(informations):
687     """
688     Return True if the value is a long.
689     """
690     if "long" in informations and informations["long"] and "unsigned" not in informations:
691         return True
692     else:
693         return False
694
695 def isUnsigned(informations):
696     """
697     Return True if the value is an unsigned.
698     """
699     if "unsigned" in informations and informations["unsigned"] and "long" not in informations:
700         return True
701     else:
702         return False
703
704 def isUnsignedLong(informations):
705     """
706     Return True if the value is an unsigned long.
707     """
708     if "unsigned" in informations and "long" in informations and informations["unsigned"] and informations["long"]:
709         return True
710     else:
711         return False
712
713 class ParseError(Exception):
714     def __init__(self, line_number, line):
715         Exception.__init__(self)
716         self.line_number = line_number
717         self.line = line
718
719 class SupportedException(Exception):
720     def __init__(self, support_string):
721         Exception.__init__(self)
722         self.support_string = support_string