4 # This file is part of BeRTOS.
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.
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.
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
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.
29 # Copyright 2008 Develer S.r.l. (http://www.develer.com/)
33 # Author: Lorenzo Berni <duplo@develer.com>
41 # Use custom copytree function
47 import DefineException
49 from _wizard_version import WIZARD_VERSION
51 from LoadException import VersionException, ToolchainException
53 def isBertosDir(directory):
54 return os.path.exists(directory + "/VERSION")
56 def bertosVersion(directory):
57 return open(directory + "/VERSION").readline().strip()
59 def enabledModules(project_info):
61 for name, module in project_info.info("MODULES").items():
63 enabled_modules.append(name)
64 return enabled_modules
66 def presetList(directory):
68 Return the list of the preset found in the selected BeRTOS Version.
70 abspath = os.path.join(directory, const.PREDEFINED_BOARDS_DIR)
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))
77 def presetInfo(preset_dir):
79 Return the preset-relevant info contined into the project_file.
81 preset_info = pickle.loads(open(os.path.join(preset_dir, "project.bertos"), "r").read())
83 description = open(os.path.join(preset_dir, "description"), "r").read()
85 # No description file found.
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"),
96 def mergeSources(srcdir, new_sources, old_sources):
97 # The current mergeSources function provide only a raw copy of the sources in the
100 # TODO: implement the three way merge algorithm
102 shutil.rmtree(srcdir, True)
103 copytree.copytree(os.path.join(new_sources, "bertos"), srcdir, ignore_list=const.IGNORE_LIST)
105 def projectFileGenerator(project_info):
106 directory = project_info.info("PROJECT_PATH")
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 if project_info.info("PRESET"):
114 # For presets save again the BERTOS_PATH into project file
115 project_data["PRESET"] = True
116 project_data["BERTOS_PATH"] = project_info.info("BERTOS_PATH")
118 # Use the local BeRTOS version instead of the original one
119 # project_data["BERTOS_PATH"] = project_info.info("BERTOS_PATH")
120 project_data["BERTOS_PATH"] = directory
121 project_data["PROJECT_NAME"] = project_info.info("PROJECT_NAME", os.path.basename(directory))
122 project_data["PROJECT_SRC_PATH"] = project_info.info("PROJECT_SRC_PATH")
123 project_data["TOOLCHAIN"] = project_info.info("TOOLCHAIN")
124 project_data["CPU_NAME"] = project_info.info("CPU_NAME")
125 project_data["SELECTED_FREQ"] = project_info.info("SELECTED_FREQ")
126 project_data["OUTPUT"] = project_info.info("OUTPUT")
127 project_data["WIZARD_VERSION"] = WIZARD_VERSION
128 project_data["PRESET"] = project_info.info("PRESET")
129 return pickle.dumps(project_data)
131 def loadPlugin(plugin):
133 Returns the given plugin module.
135 return getattr(__import__("plugins", {}, {}, [plugin]), plugin)
137 def versionFileGenerator(project_info, version_file):
138 version = bertosVersion(project_info.info("BERTOS_PATH"))
139 return version_file.replace('$version', version)
141 def userMkGenerator(project_info, destination):
142 makefile = open(os.path.join(const.DATA_DIR, "mktemplates/template.mk"), "r").read()
143 # Deadly performances loss was here :(
145 mk_data["$pname"] = os.path.basename(project_info.info("PROJECT_PATH"))
146 mk_data["$main"] = os.path.basename(project_info.info("PROJECT_PATH")) + "/main.c"
148 while makefile.find(key) != -1:
149 makefile = makefile.replace(key, mk_data[key])
150 open(destination, "w").write(makefile)
152 def mkGenerator(project_info, destination):
154 Generates the mk file for the current project.
156 makefile = open(os.path.join(const.DATA_DIR, "mktemplates/template_wiz.mk"), "r").read()
158 mk_data["$pname"] = project_info.info("PROJECT_NAME")
159 mk_data["$cpuclockfreq"] = project_info.info("SELECTED_FREQ")
160 cpu_mk_parameters = []
161 for key, value in project_info.info("CPU_INFOS").items():
162 if key.startswith(const.MK_PARAM_ID):
163 cpu_mk_parameters.append("%s = %s" %(key.replace("MK", mk_data["$pname"]), value))
164 mk_data["$cpuparameters"] = "\n".join(cpu_mk_parameters)
165 mk_data["$csrc"], mk_data["$pcsrc"], mk_data["$cppasrc"], mk_data["$cxxsrc"], mk_data["$asrc"], mk_data["$constants"] = csrcGenerator(project_info)
166 mk_data["$prefix"] = replaceSeparators(project_info.info("TOOLCHAIN")["path"].split("gcc")[0])
167 mk_data["$suffix"] = replaceSeparators(project_info.info("TOOLCHAIN")["path"].split("gcc")[1])
168 mk_data["$main"] = os.path.basename(project_info.info("PROJECT_PATH")) + "/main.c"
170 while makefile.find(key) != -1:
171 makefile = makefile.replace(key, mk_data[key])
172 open(destination, "w").write(makefile)
174 def makefileGenerator(project_info, destination):
176 Generate the Makefile for the current project.
178 makefile = open(os.path.join(const.DATA_DIR, "mktemplates/Makefile"), "r").read()
179 # TODO write a general function that works for both the mk file and the Makefile
180 while makefile.find("$pname") != -1:
181 makefile = makefile.replace("$pname", project_info.info("PROJECT_NAME"))
182 open(destination, "w").write(makefile)
184 def csrcGenerator(project_info):
185 modules = project_info.info("MODULES")
186 files = project_info.info("FILES")
187 if "harvard" in project_info.info("CPU_INFOS")["CPU_TAGS"]:
191 # file to be included in CSRC variable
193 # file to be included in PCSRC variable
195 # files to be included in CPPASRC variable
197 # files to be included in CXXSRC variable
199 # files to be included in ASRC variable
201 # constants to be included at the beginning of the makefile
203 for module, information in modules.items():
204 module_files = set([])
205 dependency_files = set([])
208 hwdir = os.path.basename(project_info.info("PROJECT_PATH")) + "/hw"
209 if information["enabled"]:
210 if "constants" in information:
211 constants.update(information["constants"])
212 cfiles, sfiles = findModuleFiles(module, project_info)
213 module_files |= set(cfiles)
214 asm_files |= set(sfiles)
215 for file in information["hw"]:
216 if file.endswith(".c"):
217 module_files |= set([hwdir + "/" + os.path.basename(file)])
218 for file_dependency in information["depends"] + tuple(files.keys()):
219 dependencyCFiles, dependencySFiles = findModuleFiles(file_dependency, project_info)
220 dependency_files |= set(dependencyCFiles)
221 asm_files |= set(dependencySFiles)
222 for file in module_files:
223 if not harvard or information.get("harvard", "both") == "both":
225 if harvard and "harvard" in information:
227 for file in dependency_files:
229 for file in project_info.info("CPU_INFOS")["C_SRC"]:
231 for file in project_info.info("CPU_INFOS")["PC_SRC"]:
233 for file in asm_files:
235 for file in project_info.info("CPU_INFOS")["CPPA_SRC"]:
237 for file in project_info.info("CPU_INFOS")["CXX_SRC"]:
239 for file in project_info.info("CPU_INFOS")["ASRC"]:
242 csrc = " \\\n\t".join(csrc) + " \\"
244 pcsrc = " \\\n\t".join(pcsrc) + " \\"
245 cppasrc = set(cppasrc)
246 cppasrc = " \\\n\t".join(cppasrc) + " \\"
248 cxxsrc = " \\\n\t".join(cxxsrc) + " \\"
250 asrc = " \\\n\t".join(asrc) + " \\"
251 constants = "\n".join([os.path.basename(project_info.info("PROJECT_PATH")) + "_" + key + " = " + unicode(value) for key, value in constants.items()])
252 return csrc, pcsrc, cppasrc, cxxsrc, asrc, constants
254 def findModuleFiles(module, project_info):
255 # Find the files related to the selected module
258 # .c files related to the module and the cpu architecture
259 for filename, path in project_info.searchFiles(module + ".c"):
260 path = path.replace(project_info.info("BERTOS_PATH") + os.sep, "")
261 path = replaceSeparators(path)
262 cfiles.append(path + "/" + filename)
263 # .s files related to the module and the cpu architecture
264 for filename, path in project_info.searchFiles(module + ".s") + \
265 project_info.searchFiles(module + ".S"):
266 path = path.replace(project_info.info("BERTOS_PATH") + os.sep, "")
267 path = replaceSeparators(path)
268 sfiles.append(path + "/" + filename)
269 # .c and .s files related to the module and the cpu tags
270 tags = project_info.info("CPU_INFOS")["CPU_TAGS"]
272 # Awful, but secure check for version
273 # TODO: split me in a method/function
275 version_string = bertosVersion(project_info.info("BERTOS_PATH"))
276 version_list = [int(i) for i in version_string.split()[-1].split('.')]
277 if version_list < [2, 5]:
278 # For older versions of BeRTOS add the toolchain to the tags
279 tags.append(project_info.info("CPU_INFOS")["TOOLCHAIN"])
281 # If the version file hasn't a valid version number do nothing
285 for filename, path in project_info.searchFiles(module + "_" + tag + ".c"):
286 path = path.replace(project_info.info("BERTOS_PATH") + os.sep, "")
288 path = replaceSeparators(path)
289 cfiles.append(path + "/" + filename)
290 for filename, path in project_info.searchFiles(module + "_" + tag + ".s") + \
291 project_info.searchFiles(module + "_" + tag + ".S"):
292 path = path.replace(project_info.info("BERTOS_PATH") + os.sep, "")
293 path = replaceSeparators(path)
294 sfiles.append(path + "/" + filename)
295 return cfiles, sfiles
297 def replaceSeparators(path):
299 Replace the separators in the given path with unix standard separator.
302 while path.find(os.sep) != -1:
303 path = path.replace(os.sep, "/")
307 path = os.environ["PATH"]
309 path = path.split(";")
311 path = path.split(":")
314 def findToolchains(path_list):
316 for element in path_list:
317 for toolchain in glob.glob(element+ "/" + const.GCC_NAME):
318 toolchains.append(toolchain)
319 return list(set(toolchains))
321 def getToolchainInfo(output):
323 expr = re.compile("Target: .*")
324 target = expr.findall(output)
326 info["target"] = target[0].split("Target: ")[1]
327 expr = re.compile("gcc version [0-9,.]*")
328 version = expr.findall(output)
329 if len(version) == 1:
330 info["version"] = version[0].split("gcc version ")[1]
331 expr = re.compile("gcc version [0-9,.]* \(.*\)")
332 build = expr.findall(output)
334 build = build[0].split("gcc version ")[1]
335 build = build[build.find("(") + 1 : build.find(")")]
336 info["build"] = build
337 expr = re.compile("Configured with: .*")
338 configured = expr.findall(output)
339 if len(configured) == 1:
340 info["configured"] = configured[0].split("Configured with: ")[1]
341 expr = re.compile("Thread model: .*")
342 thread = expr.findall(output)
344 info["thread"] = thread[0].split("Thread model: ")[1]
347 def getToolchainName(toolchain_info):
348 name = "GCC " + toolchain_info["version"] + " - " + toolchain_info["target"].strip()
351 def getTagSet(cpu_info):
354 tag_set |= set([cpu["CPU_NAME"]])
355 tag_set |= set(cpu["CPU_TAGS"])
356 tag_set |= set([cpu["TOOLCHAIN"]])
360 def getInfos(definition):
362 D.update(const.CPU_DEF)
363 def include(filename, dict = D, directory=definition[1]):
364 execfile(directory + "/" + filename, {}, D)
365 D["include"] = include
366 include(definition[0], D)
367 D["CPU_NAME"] = definition[0].split(".")[0]
368 D["DEFINITION_PATH"] = definition[1] + "/" + definition[0]
372 def getCommentList(string):
373 comment_list = re.findall(r"/\*{2}\s*([^*]*\*(?:[^/*][^*]*\*+)*)/", string)
374 comment_list = [re.findall(r"^\s*\* *(.*?)$", comment, re.MULTILINE) for comment in comment_list]
377 def loadModuleDefinition(first_comment):
379 module_definition = {}
380 for num, line in enumerate(first_comment):
381 index = line.find("$WIZ$")
385 exec line[index + len("$WIZ$ "):] in {}, module_definition
387 raise ParseError(num, line[index:])
388 elif line.find("\\brief") != -1:
389 module_definition["module_description"] = line[line.find("\\brief") + len("\\brief "):]
391 if "module_name" in module_definition:
392 module_name = module_definition[const.MODULE_DEFINITION["module_name"]]
393 del module_definition[const.MODULE_DEFINITION["module_name"]]
394 module_dict[module_name] = {}
395 if const.MODULE_DEFINITION["module_depends"] in module_definition:
396 depends = module_definition[const.MODULE_DEFINITION["module_depends"]]
397 del module_definition[const.MODULE_DEFINITION["module_depends"]]
398 if type(depends) == str:
400 module_dict[module_name]["depends"] = depends
402 module_dict[module_name]["depends"] = ()
403 if const.MODULE_DEFINITION["module_configuration"] in module_definition:
404 module_dict[module_name]["configuration"] = module_definition[const.MODULE_DEFINITION["module_configuration"]]
405 del module_definition[const.MODULE_DEFINITION["module_configuration"]]
407 module_dict[module_name]["configuration"] = ""
408 if "module_description" in module_definition:
409 module_dict[module_name]["description"] = module_definition["module_description"]
410 del module_definition["module_description"]
411 if const.MODULE_DEFINITION["module_harvard"] in module_definition:
412 harvard = module_definition[const.MODULE_DEFINITION["module_harvard"]]
413 module_dict[module_name]["harvard"] = harvard
414 del module_definition[const.MODULE_DEFINITION["module_harvard"]]
415 if const.MODULE_DEFINITION["module_hw"] in module_definition:
416 hw = module_definition[const.MODULE_DEFINITION["module_hw"]]
417 del module_definition[const.MODULE_DEFINITION["module_hw"]]
420 module_dict[module_name]["hw"] = hw
422 module_dict[module_name]["hw"] = ()
423 if const.MODULE_DEFINITION["module_supports"] in module_definition:
424 supports = module_definition[const.MODULE_DEFINITION["module_supports"]]
425 del module_definition[const.MODULE_DEFINITION["module_supports"]]
426 module_dict[module_name]["supports"] = supports
427 module_dict[module_name]["constants"] = module_definition
428 module_dict[module_name]["enabled"] = False
429 return to_be_parsed, module_dict
431 def isSupported(project, module=None, property_id=None):
432 if not module and property_id:
433 item = project.info("CONFIGURATIONS")[property_id[0]][property_id[1]]["informations"]
435 item = project.info("MODULES")[module]
436 tag_dict = project.info("ALL_CPU_TAGS")
437 if "supports" in item:
438 support_string = item["supports"]
441 exec "supported = " + support_string in tag_dict, supported
443 raise SupportedException(support_string)
444 return supported["supported"]
448 def loadDefineLists(comment_list):
450 for comment in comment_list:
451 for num, line in enumerate(comment):
452 index = line.find("$WIZ$")
455 exec line[index + len("$WIZ$ "):] in {}, define_list
457 raise ParseError(num, line[index:])
458 for key, value in define_list.items():
459 if type(value) == str:
460 define_list[key] = (value,)
463 def getDescriptionInformations(comment):
465 Take the doxygen comment and strip the wizard informations, returning the tuple
466 (comment, wizard_information)
471 for num, line in enumerate(comment):
472 index = line.find("$WIZ$")
475 brief += line[:index].strip()
477 description += " " + line[:index]
479 exec line[index + len("$WIZ$ "):] in {}, information
481 raise ParseError(num, line[index:])
484 brief += line.strip()
486 description += " " + line
487 description = description.strip()
488 return brief.strip(), description.strip(), information
490 def getDefinitionBlocks(text):
492 Take a text and return a list of tuple (description, name-value).
495 block_tmp = re.finditer(r"/\*{2}\s*([^*]*\*(?:[^/*][^*]*\*+)*)/\s*#define\s+((?:[^/]*?/?)+)\s*?(?:/{2,3}[^<].*?)?$", text, re.MULTILINE)
496 for match in block_tmp:
497 # Only the first element is needed
498 comment = match.group(1)
499 define = match.group(2)
500 start = match.start()
501 block.append(([re.findall(r"^\s*\* *(.*?)$", line, re.MULTILINE)[0] for line in comment.splitlines()], define, start))
502 for match in re.finditer(r"/{3}\s*([^<].*?)\s*#define\s+((?:[^/]*?/?)+)\s*?(?:/{2,3}[^<].*?)?$", text, re.MULTILINE):
503 comment = match.group(1)
504 define = match.group(2)
505 start = match.start()
506 block.append(([comment], define, start))
507 for match in re.finditer(r"#define\s*(.*?)\s*/{3}<\s*(.+?)\s*?(?:/{2,3}[^<].*?)?$", text, re.MULTILINE):
508 comment = match.group(2)
509 define = match.group(1)
510 start = match.start()
511 block.append(([comment], define, start))
514 def formatParamNameValue(text):
516 Take the given string and return a tuple with the name of the parameter in the first position
517 and the value in the second.
519 block = re.findall("\s*([^\s]+)\s*(.+?)\s*$", text, re.MULTILINE)
522 def loadConfigurationInfos(path):
524 Return the module configurations found in the given file as a dict with the
525 parameter name as key and a dict containig the fields above as value:
526 "value": the value of the parameter
527 "description": the description of the parameter
528 "informations": a dict containig optional informations:
529 "type": "int" | "boolean" | "enum"
530 "min": the minimum value for integer parameters
531 "max": the maximum value for integer parameters
532 "long": boolean indicating if the num is a long
533 "unsigned": boolean indicating if the num is an unsigned
534 "value_list": the name of the enum for enum parameters
535 "conditional_deps": the list of conditional dependencies for boolean parameters
537 configuration_infos = {}
538 configuration_infos["paramlist"] = []
539 for comment, define, start in getDefinitionBlocks(open(path, "r").read()):
540 name, value = formatParamNameValue(define)
541 brief, description, informations = getDescriptionInformations(comment)
542 configuration_infos["paramlist"].append((start, name))
543 configuration_infos[name] = {}
544 configuration_infos[name]["value"] = value
545 configuration_infos[name]["informations"] = informations
546 if not "type" in configuration_infos[name]["informations"]:
547 configuration_infos[name]["informations"]["type"] = findParameterType(configuration_infos[name])
548 if ("type" in configuration_infos[name]["informations"] and
549 configuration_infos[name]["informations"]["type"] == "int" and
550 configuration_infos[name]["value"].find("L") != -1):
551 configuration_infos[name]["informations"]["long"] = True
552 configuration_infos[name]["value"] = configuration_infos[name]["value"].replace("L", "")
553 if ("type" in configuration_infos[name]["informations"] and
554 configuration_infos[name]["informations"]["type"] == "int" and
555 configuration_infos[name]["value"].find("U") != -1):
556 configuration_infos[name]["informations"]["unsigned"] = True
557 configuration_infos[name]["value"] = configuration_infos[name]["value"].replace("U", "")
558 if "conditional_deps" in configuration_infos[name]["informations"]:
559 if (type(configuration_infos[name]["informations"]["conditional_deps"]) == str or
560 type(configuration_infos[name]["informations"]["conditional_deps"]) == unicode):
561 configuration_infos[name]["informations"]["conditional_deps"] = (configuration_infos[name]["informations"]["conditional_deps"], )
562 elif type(configuration_infos[name]["informations"]["conditional_deps"]) == tuple:
565 configuration_infos[name]["informations"]["conditional_deps"] = ()
566 configuration_infos[name]["description"] = description
567 configuration_infos[name]["brief"] = brief
568 return configuration_infos
570 def updateConfigurationValues(def_conf, user_conf):
571 for param in def_conf["paramlist"]:
572 if param[1] in user_conf and "value" in user_conf[param[1]]:
573 def_conf[param[1]]["value"] = user_conf[param[1]]["value"]
576 def findParameterType(parameter):
577 if "value_list" in parameter["informations"]:
579 if "min" in parameter["informations"] or "max" in parameter["informations"] or re.match(r"^\d+U?L?$", parameter["value"]) != None:
582 def sub(string, parameter, value):
584 Substitute the given value at the given parameter define in the given string
586 return re.sub(r"(?P<define>#define\s+" + parameter + r"\s+)([^\s]+)", r"\g<define>" + value, string)
588 def isInt(informations):
590 Return True if the value is a simple int.
592 if ("long" not in informatios or not informations["long"]) and ("unsigned" not in informations or informations["unsigned"]):
597 def isLong(informations):
599 Return True if the value is a long.
601 if "long" in informations and informations["long"] and "unsigned" not in informations:
606 def isUnsigned(informations):
608 Return True if the value is an unsigned.
610 if "unsigned" in informations and informations["unsigned"] and "long" not in informations:
615 def isUnsignedLong(informations):
617 Return True if the value is an unsigned long.
619 if "unsigned" in informations and "long" in informations and informations["unsigned"] and informations["long"]:
624 class ParseError(Exception):
625 def __init__(self, line_number, line):
626 Exception.__init__(self)
627 self.line_number = line_number
630 class SupportedException(Exception):
631 def __init__(self, support_string):
632 Exception.__init__(self)
633 self.support_string = support_string