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/)
32 # Author: Lorenzo Berni <duplo@develer.com>
42 import DefineException
44 from LoadException import VersionException, ToolchainException
48 from bertos_utils import (
50 isBertosDir, getTagSet, getInfos, updateConfigurationValues,
51 loadConfigurationInfos, loadDefineLists, loadModuleDefinition,
54 # Project creation functions
55 projectFileGenerator, versionFileGenerator, loadPlugin,
59 ParseError, SupportedException
63 from compatibility import updateProject
65 class BProject(object):
67 Simple class for store and retrieve project informations.
70 def __init__(self, project_file="", info_dict={}):
72 self._cached_queries = {}
76 self.loadBertosProject(project_file, info_dict)
78 #--- Load methods (methods that loads data into project) ------------------#
80 def loadBertosProject(self, project_file, info_dict):
81 project_dir = os.path.dirname(project_file)
82 project_data = pickle.loads(open(project_file, "r").read())
83 updateProject(project_data)
84 # If PROJECT_NAME is not defined it use the directory name as PROJECT_NAME
85 # NOTE: this can throw an Exception if the user has changed the directory containing the project
86 self.infos["PROJECT_NAME"] = project_data.get("PROJECT_NAME", os.path.basename(project_dir))
87 self.infos["PROJECT_PATH"] = os.path.dirname(project_file)
88 project_src_path = os.path.join(project_dir, project_data.get("PROJECT_SRC_PATH", project_data["PROJECT_NAME"]))
90 self.infos["PROJECT_SRC_PATH"] = project_src_path
93 # In projects created with older versions of the Wizard this metadata doesn't exist
94 self.infos["PROJECT_SRC_PATH"] = os.path.join(self.infos["PROJECT_PATH"], self.infos["PROJECT_NAME"])
95 self.infos["PROJECT_HW_PATH"] = os.path.join(self.infos["PROJECT_PATH"], project_data.get("PROJECT_HW_PATH", self.infos["PROJECT_PATH"]))
97 linked_sources_path = project_data["BERTOS_PATH"]
98 sources_abspath = os.path.abspath(os.path.join(project_dir, linked_sources_path))
99 project_data["BERTOS_PATH"] = sources_abspath
101 self._loadBertosSourceStuff(project_data["BERTOS_PATH"], info_dict.get("BERTOS_PATH", None))
103 self.infos["PRESET"] = project_data.get("PRESET", False)
105 # For those projects that don't have a VERSION file create a dummy one.
106 if not isBertosDir(project_dir) and not self.is_preset:
107 version_file = open(os.path.join(const.DATA_DIR, "vtemplates/VERSION"), "r").read()
108 open(os.path.join(project_dir, "VERSION"), "w").write(version_file.replace("$version", "").strip())
110 self.loadSourceTree()
111 self._loadCpuStuff(project_data["CPU_NAME"], project_data["SELECTED_FREQ"])
112 self._loadToolchainStuff(project_data["TOOLCHAIN"], info_dict.get("TOOLCHAIN", None))
113 self.infos["OUTPUT"] = project_data["OUTPUT"]
114 self.loadModuleData(True)
115 self.setEnabledModules(project_data["ENABLED_MODULES"])
117 def _loadBertosSourceStuff(self, sources_path, forced_version=None):
119 sources_path = forced_version
120 if os.path.exists(sources_path):
121 self.infos["BERTOS_PATH"] = sources_path
123 raise VersionException(self)
125 def _loadCpuStuff(self, cpu_name, cpu_frequency):
126 self.infos["CPU_NAME"] = cpu_name
127 cpu_info = self.getCpuInfos()
129 if cpu["CPU_NAME"] == cpu_name:
130 self.infos["CPU_INFOS"] = cpu
132 tag_list = getTagSet(cpu_info)
133 # Create, fill and store the dict with the tags
135 for element in tag_list:
136 tag_dict[element] = False
137 infos = self.info("CPU_INFOS")
139 if tag in infos["CPU_TAGS"] + [infos["CPU_NAME"], infos["TOOLCHAIN"]]:
142 tag_dict[tag] = False
143 self.infos["ALL_CPU_TAGS"] = tag_dict
144 self.infos["SELECTED_FREQ"] = cpu_frequency
146 def _loadToolchainStuff(self, toolchain, forced_toolchain=None):
147 toolchain = toolchain
149 toolchain = forced_toolchain
150 if os.path.exists(toolchain["path"]):
151 self.infos["TOOLCHAIN"] = toolchain
153 raise ToolchainException(self)
155 def loadProjectFromPreset(self, preset):
157 Load a project from a preset.
158 NOTE: this is a stub.
160 project_file = os.path.join(preset, "project.bertos")
161 project_data = pickle.loads(open(project_file, "r").read())
162 self.loadSourceTree()
163 self._loadCpuStuff(project_data["CPU_NAME"], project_data["SELECTED_FREQ"])
165 # NOTE: this is a HACK!!!
166 # TODO: find a better way to reuse loadModuleData
167 preset_project_name = project_data.get("PROJECT_NAME", os.path.basename(preset))
168 preset_prj_src_path = os.path.join(preset, project_data.get("PROJECT_SRC_PATH", os.path.join(preset, preset_project_name)))
169 preset_prj_hw_path = os.path.join(preset, project_data.get("PROJECT_HW_PATH", preset))
171 old_project_name = self.infos["PROJECT_NAME"]
172 old_project_path = self.infos["PROJECT_PATH"]
173 old_project_src_path = self.infos["PROJECT_SRC_PATH"]
174 old_project_hw_path = self.infos["PROJECT_HW_PATH"]
176 self.infos["PROJECT_NAME"] = preset_project_name
177 self.infos["PROJECT_PATH"] = preset
178 self.infos["PROJECT_SRC_PATH"] = preset_prj_src_path
179 self.infos["PROJECT_HW_PATH"] = preset_prj_hw_path
181 self.loadModuleData(True)
182 self.setEnabledModules(project_data["ENABLED_MODULES"])
184 self.infos["PROJECT_NAME"] = old_project_name
185 self.infos["PROJECT_PATH"] = old_project_path
186 self.infos["PROJECT_SRC_PATH"] = old_project_src_path
187 self.infos["PROJECT_HW_PATH"] = old_project_hw_path
188 # End of the ugly HACK!
190 self.infos["PRESET_NAME"] = preset_project_name
191 self.infos["PRESET_PATH"] = preset
192 self.infos["PRESET_SRC_PATH"] = preset_prj_src_path
193 self.infos["PRESET_HW_PATH"] = preset_prj_hw_path
195 def loadProjectPresets(self):
197 Load the default presets (into the const.PREDEFINED_BOARDS_DIR).
199 # NOTE: this method does nothing (for now).
200 preset_path = os.path.join(self.infos["BERTOS_PATH"], const.PREDEFINED_BOARDS_DIR)
201 preset_tree = {"children": []}
202 if os.path.exists(preset_path):
203 preset_tree = self._loadProjectPresetTree(preset_path)
204 self.infos["PRESET_TREE"] = preset_tree
206 def _loadProjectPresetTree(self, path):
208 _tree["info"] = self._loadPresetInfo(os.path.join(path, const.PREDEFINED_BOARD_SPEC_FILE))
209 _tree["info"]["filename"] = os.path.basename(path)
210 _tree["info"]["path"] = path
211 _tree["children"] = {}
212 entries = set(os.listdir(path))
213 for entry in entries:
214 _path = os.path.join(path, entry)
215 if os.path.isdir(_path):
216 sub_entries = set(os.listdir(_path))
217 if const.PREDEFINED_BOARD_SPEC_FILE in sub_entries:
218 _tree["children"][_path] = self._loadProjectPresetTree(_path)
219 # Add into the info dict the dir type (dir/project)
220 if _tree["children"]:
221 _tree["info"]["type"] = "dir"
223 _tree["info"]["type"] = "project"
226 def _loadPresetInfo(self, preset_spec_file):
229 execfile(preset_spec_file, {}, D)
234 def loadModuleData(self, edit=False):
235 module_info_dict = {}
237 configuration_info_dict = {}
239 for filename, path in self.findDefinitions("*.h") + self.findDefinitions("*.c") + self.findDefinitions("*.s") + self.findDefinitions("*.S"):
240 comment_list = getCommentList(open(path + "/" + filename, "r").read())
241 if len(comment_list) > 0:
243 configuration_info = {}
245 to_be_parsed, module_dict = loadModuleDefinition(comment_list[0])
246 except ParseError, err:
247 raise DefineException.ModuleDefineException(os.path.join(path, filename), err.line_number, err.line)
248 for module, information in module_dict.items():
249 if "depends" not in information:
250 information["depends"] = ()
251 information["depends"] += (filename.split(".")[0],)
252 information["category"] = os.path.basename(path)
254 # Hack to remove 'bertos/' from the configuration file path.
256 # The new module information format substitute paths like 'bertos/cfg/config_file.h'
257 # with the relative path into the bertos directory ('cfg/config_file.h')
258 information["configuration"] = information["configuration"].replace("bertos/", "")
259 information["hw"] = [hw.replace("bertos/", "") for hw in information["hw"]]
261 if "configuration" in information and len(information["configuration"]):
262 configuration = module_dict[module]["configuration"]
264 cfg_file_path = os.path.join(self.bertos_srcdir, configuration)
265 configuration_info[configuration] = loadConfigurationInfos(cfg_file_path)
266 except ParseError, err:
267 raise DefineException.ConfigurationDefineException(cfg_file_path, err.line_number, err.line)
270 path = self.infos["PROJECT_SRC_PATH"]
271 cfg_file_path = os.path.join(path, configuration)
272 configuration_info[configuration] = updateConfigurationValues(configuration_info[configuration], loadConfigurationInfos(cfg_file_path))
273 except ParseError, err:
274 raise DefineException.ConfigurationDefineException(cfg_file_path, err.line_number, err.line)
276 # The wizard can't find the file, use the default configuration
278 module_info_dict.update(module_dict)
279 configuration_info_dict.update(configuration_info)
282 list_dict = loadDefineLists(comment_list[1:])
283 list_info_dict.update(list_dict)
284 except ParseError, err:
285 raise DefineException.EnumDefineException(os.path.join(path, filename), err.line_number, err.line)
286 for tag in self.infos["CPU_INFOS"]["CPU_TAGS"]:
287 for filename, path in self.findDefinitions("*_" + tag + ".h"):
288 comment_list = getCommentList(open(path + "/" + filename, "r").read())
289 list_info_dict.update(loadDefineLists(comment_list))
290 self.infos["MODULES"] = module_info_dict
291 self.infos["LISTS"] = list_info_dict
292 self.infos["CONFIGURATIONS"] = configuration_info_dict
293 self.infos["FILES"] = file_dict
295 def loadSourceTree(self):
297 Index BeRTOS source and load it in memory.
299 # Index only the BERTOS_PATH/bertos content
300 bertos_sources_dir = os.path.join(self.info("BERTOS_PATH"), "bertos")
302 if os.path.exists(bertos_sources_dir):
303 for element in os.walk(bertos_sources_dir):
305 file_dict[f] = file_dict.get(f, []) + [element[0]]
306 self.infos["FILE_DICT"] = file_dict
308 def reloadCpuInfo(self):
309 for cpu_info in self.getCpuInfos():
310 if cpu_info["CPU_NAME"] == self.infos["CPU_NAME"]:
311 self.infos["CPU_INFOS"] = cpu_info
313 #-------------------------------------------------------------------------#
315 def createBertosProject(self):
316 # NOTE: Temporary hack.
318 self._editBertosProject()
320 if not self.from_preset:
321 self._newCustomBertosProject()
323 self._newBertosProjectFromPreset()
325 def _newBertosProject(self):
326 for directory in (self.maindir, self.srcdir, self.prjdir, self.cfgdir, self.hwdir):
327 self._createDirectory(directory)
328 # Write the project file
329 self._writeProjectFile(os.path.join(self.maindir, "project.bertos"))
331 self._writeVersionFile(os.path.join(self.maindir, "VERSION"))
332 # Destination makefile
333 self._writeMakefile()
335 self._copySources(self.bertos_maindir, self.srcdir)
336 # Set properly the autoenabled parameters
337 self._setupAutoenabledParameters()
338 # Copy all the configuration files
339 self._writeCfgFiles(self.bertos_srcdir, self.cfgdir)
340 # Destination wizard mk file
341 self._writeWizardMkFile()
343 def _newCustomBertosProject(self):
344 # Create/write/copy the common things
345 self._newBertosProject()
346 # Copy the clean hw files
347 self._createDirectory(self.hwdir)
348 # Copy all the hw files
349 self._writeHwFiles(self.bertos_srcdir, self.hwdir)
350 # Destination user mk file
351 self._writeUserMkFile()
352 # Destination main.c file
353 self._writeMainFile(self.prjdir + "/main.c")
354 # Create project files for selected plugins
355 self._createProjectFiles()
357 def _newBertosProjectFromPreset(self):
358 # Create/write/copy the common things
359 self._newBertosProject()
361 # Copy all the files and dirs except cfg/hw/*.mk
362 self._writeCustomSrcFiles()
365 self._writeAllPresetHwFiles(self.src_hwdir, self.hwdir)
367 # Copyt the new *_user.mk file
368 self._writeUserMkFileFromPreset()
370 # Create project files for selected plugins
371 self._createProjectFiles()
373 def _editBertosProject(self):
374 # Write the project file
375 self._writeProjectFile(os.path.join(self.maindir, "project.bertos"))
376 if not self.is_preset:
377 # Generate this files only if the project isn't a preset
379 self._writeVersionFile(os.path.join(self.maindir, "VERSION"))
380 # Destination makefile
381 self._writeMakefile()
383 self._mergeSources(self.bertos_maindir, self.srcdir, self.old_srcdir)
384 # Copy all the hw files
385 self._writeHwFiles(self.bertos_srcdir, self.hwdir)
386 # Destination wizard mk file
387 self._writeWizardMkFile()
388 # Set properly the autoenabled parameters
389 self._setupAutoenabledParameters()
390 # Copy all the configuration files
391 self._writeCfgFiles(self.bertos_srcdir, self.cfgdir)
392 if not self.is_preset:
393 # Create project files for selected plugins only if the project isn't a preset
394 self._createProjectFiles()
396 def _createProjectFiles(self):
397 # Files for selected plugins
399 for plugin in self.infos["OUTPUT"]:
400 module = loadPlugin(plugin)
401 relevants_files[plugin] = module.createProject(self)
402 self.infos["RELEVANT_FILES"] = relevants_files
404 def _writeVersionFile(self, filename):
405 if not self.edit or self.old_srcdir:
406 version_file = open(os.path.join(const.DATA_DIR, "vtemplates/VERSION"), "r").read()
407 open(filename, "w").write(versionFileGenerator(self, version_file))
409 def _writeProjectFile(self, filename):
410 f = open(filename, "w")
411 f.write(projectFileGenerator(self))
414 def _writeMakefile(self):
415 bertos_utils.makefileGenerator(self)
417 def _writeUserMkFile(self):
418 bertos_utils.userMkGenerator(self)
420 def _writeUserMkFileFromPreset(self):
421 bertos_utils.userMkGeneratorFromPreset(self)
423 def _writeWizardMkFile(self):
424 bertos_utils.mkGenerator(self)
426 def _writeMainFile(self, filename):
427 main = open(os.path.join(const.DATA_DIR, "srctemplates/main.c"), "r").read()
428 open(filename, "w").write(main)
430 def _writeHwFiles(self, source_dir, destination_dir):
431 for module, information in self.infos["MODULES"].items():
432 for hwfile in information["hw"]:
435 string = open(source_dir + "/" + hwfile, "r").read()
436 hwfile_path = destination_dir + "/" + os.path.basename(hwfile)
437 if not self.edit or not os.path.exists(hwfile_path):
438 # If not in editing mode it copies all the hw files. If in
439 # editing mode it copies only the files that don't exist yet
440 open(os.path.join(destination_dir,os.path.basename(hwfile)), "w").write(string)
442 def _writeAllPresetHwFiles(self, source_dir, destination_dir):
444 Copy all but directories contained into the preset hw directory.
445 It's needed because some presets need custom hw files not defined with
446 Wizard directives into modules...
448 source_dir = os.path.join(source_dir, "hw")
449 for f in os.listdir(source_dir):
450 abspath = os.path.join(source_dir, f)
451 if not os.path.isdir(abspath):
452 # Exlude directories from the copy!
453 hw_file = open(os.path.join(source_dir, f), 'r').read()
454 open(os.path.join(destination_dir, f), 'w').write(hw_file)
456 def _writeCfgFiles(self, source_dir, destination_dir):
457 for configuration, information in self.infos["CONFIGURATIONS"].items():
458 string = open(source_dir + "/" + configuration, "r").read()
459 for start, parameter in information["paramlist"]:
460 infos = information[parameter]
461 value = infos["value"]
462 if "unsigned" in infos["informations"] and infos["informations"]["unsigned"]:
464 if "long" in infos["informations"] and infos["informations"]["long"]:
466 string = sub(string, parameter, value)
467 f = open(os.path.join(destination_dir, os.path.basename(configuration)), "w")
471 def _writeCustomSrcFiles(self):
472 origin = self.infos["PRESET_SRC_PATH"]
473 # Files to be ignored (all project files, cfg dir, wizard mk file, all global ignored dirs)
474 project_related_stuff = (
477 self.infos["PRESET_NAME"] + ".mk",
478 self.infos["PRESET_NAME"] + "_user.mk",
480 self.infos["PRESET_NAME"] + ".project",
481 self.infos["PRESET_NAME"] + ".workspace",
482 ) + const.IGNORE_LIST
483 for element in os.listdir(origin):
484 if element not in project_related_stuff:
485 full_path = os.path.join(origin, element)
486 if os.path.isdir(full_path):
487 copytree.copytree(full_path, os.path.join(self.prjdir, element), ignore_list=const.IGNORE_LIST)
489 shutil.copy(full_path, self.prjdir)
491 def _setupAutoenabledParameters(self):
492 for module, information in self.infos["MODULES"].items():
493 if "configuration" in information and information["configuration"] != "":
494 configurations = self.infos["CONFIGURATIONS"]
495 configuration = configurations[information["configuration"]]
496 for start, parameter in configuration["paramlist"]:
497 if "type" in configuration[parameter]["informations"] and configuration[parameter]["informations"]["type"] == "autoenabled":
498 configuration[parameter]["value"] = "1" if information["enabled"] else "0"
499 self.infos["CONFIGURATIONS"] = configurations
501 # Project related properties
504 return self.infos.get("PROJECT_PATH", None)
509 return os.path.join(self.maindir, "bertos")
515 return self.infos.get("PROJECT_SRC_PATH", None)
520 return os.path.join(self.prjdir, "hw")
527 return os.path.join(self.prjdir, "cfg")
532 def old_srcdir(self):
533 return self.infos.get("OLD_BERTOS_PATH", None)
535 # BeRTOS sources related properties
537 def bertos_maindir(self):
538 return self.infos.get("BERTOS_PATH", None)
541 def bertos_srcdir(self):
542 if self.bertos_maindir:
543 return os.path.join(self.bertos_maindir, "bertos")
550 return os.path.join(self.infos["PRESET_PATH"], self.infos["PRESET_HW_PATH"])
552 return self.bertos_maindir
555 def from_preset(self):
556 return self.infos.get("PROJECT_FROM_PRESET", False)
560 return self.infos.get("PRESET", False)
562 def _createDirectory(self, directory):
565 if os.path.isdir(directory):
566 shutil.rmtree(directory, True)
567 os.makedirs(directory)
569 def _copySources(self, origin, destination):
570 # If not in editing mode it copies all the bertos sources in the /bertos subdirectory of the project
571 shutil.rmtree(destination, True)
572 copytree.copytree(origin + "/bertos", destination, ignore_list=const.IGNORE_LIST)
574 def _mergeSources(self, origin, destination, old_sources_dir):
576 mergeSources(destination, origin, old_sources_dir)
578 def setInfo(self, key, value):
580 Store the given value with the name key.
582 self.infos[key] = value
584 def info(self, key, default=None):
586 Retrieve the value associated with the name key.
588 if key in self.infos:
589 return copy.deepcopy(self.infos[key])
592 def getCpuInfos(self):
594 for definition in self.findDefinitions(const.CPU_DEFINITION):
595 cpuInfos.append(getInfos(definition))
598 def searchFiles(self, filename):
599 file_dict = self.infos["FILE_DICT"]
600 return [(filename, dirname) for dirname in file_dict.get(filename, [])]
602 def findDefinitions(self, ftype):
603 # Maintain a cache for every scanned BERTOS_PATH
604 definitions_dict = self._cached_queries.get(self.infos["BERTOS_PATH"], {})
605 definitions = definitions_dict.get(ftype, None)
606 if definitions is not None:
608 file_dict = self.infos["FILE_DICT"]
610 for filename in file_dict:
611 if fnmatch.fnmatch(filename, ftype):
612 definitions += [(filename, dirname) for dirname in file_dict.get(filename, [])]
614 # If no cache for the current BERTOS_PATH create an empty one
615 if not definitions_dict:
616 self._cached_queries[self.infos["BERTOS_PATH"]] = {}
617 # Fill the empty cache with the result
618 self._cached_queries[self.infos["BERTOS_PATH"]][ftype] = definitions
621 def setEnabledModules(self, enabled_modules):
622 modules = self.infos["MODULES"]
624 for module, information in modules.items():
625 information["enabled"] = module in enabled_modules
626 if information["enabled"]:
627 for dependency in information["depends"]:
628 if not dependency in modules:
629 files[dependency] = files.get(dependency, 0) + 1
630 self.infos["MODULES"] = modules
631 self.infos["FILES"] = files
634 return "<BProject:instance %d>%s" %(id(self), repr(self.infos))