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)
387 # Destination wizard mk file (it seems that this file need to be
388 # rewritten also if the project is a preset)...
389 self._writeWizardMkFile()
391 # Set properly the autoenabled parameters
392 self._setupAutoenabledParameters()
393 # Copy all the configuration files
394 self._writeCfgFiles(self.bertos_srcdir, self.cfgdir)
395 if not self.is_preset:
396 # Create project files for selected plugins only if the project isn't a preset
397 self._createProjectFiles()
399 def _createProjectFiles(self):
400 # Files for selected plugins
402 for plugin in self.infos["OUTPUT"]:
403 module = loadPlugin(plugin)
404 relevants_files[plugin] = module.createProject(self)
405 self.infos["RELEVANT_FILES"] = relevants_files
407 def _writeVersionFile(self, filename):
408 if not self.edit or self.old_srcdir:
409 version_file = open(os.path.join(const.DATA_DIR, "vtemplates/VERSION"), "r").read()
410 open(filename, "w").write(versionFileGenerator(self, version_file))
412 def _writeProjectFile(self, filename):
413 f = open(filename, "w")
414 f.write(projectFileGenerator(self))
417 def _writeMakefile(self):
418 bertos_utils.makefileGenerator(self)
420 def _writeUserMkFile(self):
421 bertos_utils.userMkGenerator(self)
423 def _writeUserMkFileFromPreset(self):
424 bertos_utils.userMkGeneratorFromPreset(self)
426 def _writeWizardMkFile(self):
427 bertos_utils.mkGenerator(self)
429 def _writeMainFile(self, filename):
430 main = open(os.path.join(const.DATA_DIR, "srctemplates/main.c"), "r").read()
431 open(filename, "w").write(main)
433 def _writeHwFiles(self, source_dir, destination_dir):
434 for module, information in self.infos["MODULES"].items():
435 for hwfile in information["hw"]:
438 string = open(source_dir + "/" + hwfile, "r").read()
439 hwfile_path = destination_dir + "/" + os.path.basename(hwfile)
440 if not self.edit or not os.path.exists(hwfile_path):
441 # If not in editing mode it copies all the hw files. If in
442 # editing mode it copies only the files that don't exist yet
443 open(os.path.join(destination_dir,os.path.basename(hwfile)), "w").write(string)
445 def _writeAllPresetHwFiles(self, source_dir, destination_dir):
447 Copy all but directories contained into the preset hw directory.
448 It's needed because some presets need custom hw files not defined with
449 Wizard directives into modules...
451 source_dir = os.path.join(source_dir, "hw")
452 for f in os.listdir(source_dir):
453 abspath = os.path.join(source_dir, f)
454 if not os.path.isdir(abspath):
455 # Exlude directories from the copy!
456 hw_file = open(os.path.join(source_dir, f), 'r').read()
457 open(os.path.join(destination_dir, f), 'w').write(hw_file)
459 def _writeCfgFiles(self, source_dir, destination_dir):
460 for configuration, information in self.infos["CONFIGURATIONS"].items():
461 string = open(source_dir + "/" + configuration, "r").read()
462 for start, parameter in information["paramlist"]:
463 infos = information[parameter]
464 value = infos["value"]
465 if "unsigned" in infos["informations"] and infos["informations"]["unsigned"]:
467 if "long" in infos["informations"] and infos["informations"]["long"]:
469 string = sub(string, parameter, value)
470 f = open(os.path.join(destination_dir, os.path.basename(configuration)), "w")
474 def _writeCustomSrcFiles(self):
475 origin = self.infos["PRESET_SRC_PATH"]
476 # Files to be ignored (all project files, cfg dir, wizard mk file, all global ignored dirs)
477 project_related_stuff = (
480 self.infos["PRESET_NAME"] + ".mk",
481 self.infos["PRESET_NAME"] + "_user.mk",
483 self.infos["PRESET_NAME"] + ".project",
484 self.infos["PRESET_NAME"] + ".workspace",
485 ) + const.IGNORE_LIST
486 for element in os.listdir(origin):
487 if element not in project_related_stuff:
488 full_path = os.path.join(origin, element)
489 if os.path.isdir(full_path):
490 copytree.copytree(full_path, os.path.join(self.prjdir, element), ignore_list=const.IGNORE_LIST)
492 shutil.copy(full_path, self.prjdir)
494 def _setupAutoenabledParameters(self):
495 for module, information in self.infos["MODULES"].items():
496 if "configuration" in information and information["configuration"] != "":
497 configurations = self.infos["CONFIGURATIONS"]
498 configuration = configurations[information["configuration"]]
499 for start, parameter in configuration["paramlist"]:
500 if "type" in configuration[parameter]["informations"] and configuration[parameter]["informations"]["type"] == "autoenabled":
501 configuration[parameter]["value"] = "1" if information["enabled"] else "0"
502 self.infos["CONFIGURATIONS"] = configurations
504 # Project related properties
507 return self.infos.get("PROJECT_PATH", None)
512 return os.path.join(self.maindir, "bertos")
518 return self.infos.get("PROJECT_SRC_PATH", None)
523 return os.path.join(self.prjdir, "hw")
530 return os.path.join(self.prjdir, "cfg")
535 def old_srcdir(self):
536 return self.infos.get("OLD_BERTOS_PATH", None)
538 # BeRTOS sources related properties
540 def bertos_maindir(self):
541 return self.infos.get("BERTOS_PATH", None)
544 def bertos_srcdir(self):
545 if self.bertos_maindir:
546 return os.path.join(self.bertos_maindir, "bertos")
553 return os.path.join(self.infos["PRESET_PATH"], self.infos["PRESET_HW_PATH"])
555 return self.bertos_maindir
558 def from_preset(self):
559 return self.infos.get("PROJECT_FROM_PRESET", False)
563 return self.infos.get("PRESET", False)
565 def _createDirectory(self, directory):
568 if os.path.isdir(directory):
569 shutil.rmtree(directory, True)
570 os.makedirs(directory)
572 def _copySources(self, origin, destination):
573 # If not in editing mode it copies all the bertos sources in the /bertos subdirectory of the project
574 shutil.rmtree(destination, True)
575 copytree.copytree(origin + "/bertos", destination, ignore_list=const.IGNORE_LIST)
577 def _mergeSources(self, origin, destination, old_sources_dir):
579 mergeSources(destination, origin, old_sources_dir)
581 def setInfo(self, key, value):
583 Store the given value with the name key.
585 self.infos[key] = value
587 def info(self, key, default=None):
589 Retrieve the value associated with the name key.
591 if key in self.infos:
592 return copy.deepcopy(self.infos[key])
595 def getCpuInfos(self):
597 for definition in self.findDefinitions(const.CPU_DEFINITION):
598 cpuInfos.append(getInfos(definition))
601 def searchFiles(self, filename):
602 file_dict = self.infos["FILE_DICT"]
603 return [(filename, dirname) for dirname in file_dict.get(filename, [])]
605 def findDefinitions(self, ftype):
606 # Maintain a cache for every scanned BERTOS_PATH
607 definitions_dict = self._cached_queries.get(self.infos["BERTOS_PATH"], {})
608 definitions = definitions_dict.get(ftype, None)
609 if definitions is not None:
611 file_dict = self.infos["FILE_DICT"]
613 for filename in file_dict:
614 if fnmatch.fnmatch(filename, ftype):
615 definitions += [(filename, dirname) for dirname in file_dict.get(filename, [])]
617 # If no cache for the current BERTOS_PATH create an empty one
618 if not definitions_dict:
619 self._cached_queries[self.infos["BERTOS_PATH"]] = {}
620 # Fill the empty cache with the result
621 self._cached_queries[self.infos["BERTOS_PATH"]][ftype] = definitions
624 def setEnabledModules(self, enabled_modules):
625 modules = self.infos["MODULES"]
627 for module, information in modules.items():
628 information["enabled"] = module in enabled_modules
629 if information["enabled"]:
630 for dependency in information["depends"]:
631 if not dependency in modules:
632 files[dependency] = files.get(dependency, 0) + 1
633 self.infos["MODULES"] = modules
634 self.infos["FILES"] = files
637 return "<BProject:instance %d>%s" %(id(self), repr(self.infos))