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>
43 import DefineException
45 from LoadException import VersionException, ToolchainException
49 from bertos_utils import (
51 isBertosDir, getTagSet, getInfos, updateConfigurationValues,
52 loadConfigurationInfos, loadDefineLists, loadModuleDefinition,
55 # Project creation functions
56 projectFileGenerator, versionFileGenerator, loadPlugin,
60 ParseError, SupportedException
64 from compatibility import updateProject
66 class BProject(object):
68 Simple class for store and retrieve project informations.
71 def __init__(self, project_file="", info_dict={}):
73 self._cached_queries = {}
77 self.loadBertosProject(project_file, info_dict)
79 #--- Load methods (methods that loads data into project) ------------------#
81 def loadBertosProject(self, project_file, info_dict):
82 project_dir = os.path.dirname(project_file)
83 project_data = pickle.loads(open(project_file, "r").read())
84 updateProject(project_data)
85 # If PROJECT_NAME is not defined it use the directory name as PROJECT_NAME
86 # NOTE: this can throw an Exception if the user has changed the directory containing the project
87 self.infos["PROJECT_NAME"] = project_data.get("PROJECT_NAME", os.path.basename(project_dir))
88 self.infos["PROJECT_PATH"] = os.path.dirname(project_file)
89 project_src_path = os.path.join(project_dir, project_data.get("PROJECT_SRC_PATH", project_data["PROJECT_NAME"]))
91 self.infos["PROJECT_SRC_PATH"] = project_src_path
94 # In projects created with older versions of the Wizard this metadata doesn't exist
95 self.infos["PROJECT_SRC_PATH"] = os.path.join(self.infos["PROJECT_PATH"], self.infos["PROJECT_NAME"])
96 self.infos["PROJECT_HW_PATH"] = os.path.join(self.infos["PROJECT_PATH"], project_data.get("PROJECT_HW_PATH", self.infos["PROJECT_PATH"]))
98 linked_sources_path = project_data["BERTOS_PATH"]
99 sources_abspath = os.path.abspath(os.path.join(project_dir, linked_sources_path))
100 project_data["BERTOS_PATH"] = sources_abspath
102 self._loadBertosSourceStuff(project_data["BERTOS_PATH"], info_dict.get("BERTOS_PATH", None))
104 self.infos["PRESET"] = project_data.get("PRESET", False)
106 # For those projects that don't have a VERSION file create a dummy one.
107 if not isBertosDir(project_dir) and not self.is_preset:
108 version_file = open(os.path.join(const.DATA_DIR, "vtemplates/VERSION"), "r").read()
109 open(os.path.join(project_dir, "VERSION"), "w").write(version_file.replace("$version", "").strip())
111 self.loadSourceTree()
112 self._loadCpuStuff(project_data["CPU_NAME"], project_data["SELECTED_FREQ"])
113 self._loadToolchainStuff(project_data["TOOLCHAIN"], info_dict.get("TOOLCHAIN", None))
114 self.infos["OUTPUT"] = project_data["OUTPUT"]
115 self.loadModuleData(True)
116 self.setEnabledModules(project_data["ENABLED_MODULES"])
118 def _loadBertosSourceStuff(self, sources_path, forced_version=None):
120 sources_path = forced_version
121 if os.path.exists(sources_path):
122 self.infos["BERTOS_PATH"] = sources_path
124 raise VersionException(self)
126 def _loadCpuStuff(self, cpu_name, cpu_frequency):
127 self.infos["CPU_NAME"] = cpu_name
128 cpu_info = self.getCpuInfos()
130 if cpu["CPU_NAME"] == cpu_name:
131 self.infos["CPU_INFOS"] = cpu
133 tag_list = getTagSet(cpu_info)
134 # Create, fill and store the dict with the tags
136 for element in tag_list:
137 tag_dict[element] = False
138 infos = self.info("CPU_INFOS")
140 if tag in infos["CPU_TAGS"] + [infos["CPU_NAME"], infos["TOOLCHAIN"]]:
143 tag_dict[tag] = False
144 self.infos["ALL_CPU_TAGS"] = tag_dict
145 self.infos["SELECTED_FREQ"] = cpu_frequency
147 def _loadToolchainStuff(self, toolchain, forced_toolchain=None):
148 toolchain = toolchain
150 toolchain = forced_toolchain
151 if os.path.exists(toolchain["path"]):
152 self.infos["TOOLCHAIN"] = toolchain
154 raise ToolchainException(self)
156 def loadProjectFromPreset(self, preset):
158 Load a project from a preset.
159 NOTE: this is a stub.
161 project_file = os.path.join(preset, "project.bertos")
162 project_data = pickle.loads(open(project_file, "r").read())
163 self.loadSourceTree()
164 self._loadCpuStuff(project_data["CPU_NAME"], project_data["SELECTED_FREQ"])
166 # NOTE: this is a HACK!!!
167 # TODO: find a better way to reuse loadModuleData
168 preset_project_name = project_data.get("PROJECT_NAME", os.path.basename(preset))
169 preset_prj_src_path = os.path.join(preset, project_data.get("PROJECT_SRC_PATH", os.path.join(preset, preset_project_name)))
170 preset_prj_hw_path = os.path.join(preset, project_data.get("PROJECT_HW_PATH", preset))
172 old_project_name = self.infos["PROJECT_NAME"]
173 old_project_path = self.infos["PROJECT_PATH"]
174 old_project_src_path = self.infos["PROJECT_SRC_PATH"]
175 old_project_hw_path = self.infos["PROJECT_HW_PATH"]
177 self.infos["PROJECT_NAME"] = preset_project_name
178 self.infos["PROJECT_PATH"] = preset
179 self.infos["PROJECT_SRC_PATH"] = preset_prj_src_path
180 self.infos["PROJECT_HW_PATH"] = preset_prj_hw_path
182 self.loadModuleData(True)
183 self.setEnabledModules(project_data["ENABLED_MODULES"])
185 self.infos["PROJECT_NAME"] = old_project_name
186 self.infos["PROJECT_PATH"] = old_project_path
187 self.infos["PROJECT_SRC_PATH"] = old_project_src_path
188 self.infos["PROJECT_HW_PATH"] = old_project_hw_path
189 # End of the ugly HACK!
191 self.infos["PRESET_NAME"] = preset_project_name
192 self.infos["PRESET_PATH"] = preset
193 self.infos["PRESET_SRC_PATH"] = preset_prj_src_path
194 self.infos["PRESET_HW_PATH"] = preset_prj_hw_path
196 def loadProjectPresets(self):
198 Load the default presets (into the const.PREDEFINED_BOARDS_DIR).
200 # NOTE: this method does nothing (for now).
201 preset_path = os.path.join(self.infos["BERTOS_PATH"], const.PREDEFINED_BOARDS_DIR)
202 preset_tree = {"children": []}
203 if os.path.exists(preset_path):
204 preset_tree = self._loadProjectPresetTree(preset_path)
205 self.infos["PRESET_TREE"] = preset_tree
207 def _loadProjectPresetTree(self, path):
209 _tree["info"] = self._loadPresetInfo(os.path.join(path, const.PREDEFINED_BOARD_SPEC_FILE))
210 _tree["info"]["filename"] = os.path.basename(path)
211 _tree["info"]["path"] = path
212 _tree["children"] = {}
213 entries = set(os.listdir(path))
214 for entry in entries:
215 _path = os.path.join(path, entry)
216 if os.path.isdir(_path):
217 sub_entries = set(os.listdir(_path))
218 if const.PREDEFINED_BOARD_SPEC_FILE in sub_entries:
219 _tree["children"][_path] = self._loadProjectPresetTree(_path)
220 # Add into the info dict the dir type (dir/project)
221 if _tree["children"]:
222 _tree["info"]["type"] = "dir"
224 _tree["info"]["type"] = "project"
227 def _loadPresetInfo(self, preset_spec_file):
230 execfile(preset_spec_file, {}, D)
235 def loadModuleData(self, edit=False):
236 module_info_dict = {}
238 configuration_info_dict = {}
240 for filename, path in self.findDefinitions("*.h") + self.findDefinitions("*.c") + self.findDefinitions("*.s") + self.findDefinitions("*.S"):
241 comment_list = getCommentList(open(path + "/" + filename, "r").read())
242 if len(comment_list) > 0:
244 configuration_info = {}
246 to_be_parsed, module_dict = loadModuleDefinition(comment_list[0])
247 except ParseError, err:
248 raise DefineException.ModuleDefineException(os.path.join(path, filename), err.line_number, err.line)
249 for module, information in module_dict.items():
250 if "depends" not in information:
251 information["depends"] = ()
252 information["depends"] += (filename.split(".")[0],)
253 information["category"] = os.path.basename(path)
255 # Hack to remove 'bertos/' from the configuration file path.
257 # The new module information format substitute paths like 'bertos/cfg/config_file.h'
258 # with the relative path into the bertos directory ('cfg/config_file.h')
259 information["configuration"] = information["configuration"].replace("bertos/", "")
260 information["hw"] = [hw.replace("bertos/", "") for hw in information["hw"]]
262 if "configuration" in information and len(information["configuration"]):
263 configuration = module_dict[module]["configuration"]
265 cfg_file_path = os.path.join(self.bertos_srcdir, configuration)
266 configuration_info[configuration] = loadConfigurationInfos(cfg_file_path)
267 except ParseError, err:
268 raise DefineException.ConfigurationDefineException(cfg_file_path, err.line_number, err.line)
271 path = self.infos["PROJECT_SRC_PATH"]
272 cfg_file_path = os.path.join(path, configuration)
273 configuration_info[configuration] = updateConfigurationValues(configuration_info[configuration], loadConfigurationInfos(cfg_file_path))
274 except ParseError, err:
275 raise DefineException.ConfigurationDefineException(cfg_file_path, err.line_number, err.line)
277 # The wizard can't find the file, use the default configuration
279 module_info_dict.update(module_dict)
280 configuration_info_dict.update(configuration_info)
283 list_dict = loadDefineLists(comment_list[1:])
284 list_info_dict.update(list_dict)
285 except ParseError, err:
286 raise DefineException.EnumDefineException(os.path.join(path, filename), err.line_number, err.line)
287 for tag in self.infos["CPU_INFOS"]["CPU_TAGS"]:
288 for filename, path in self.findDefinitions("*_" + tag + ".h"):
289 comment_list = getCommentList(open(path + "/" + filename, "r").read())
290 list_info_dict.update(loadDefineLists(comment_list))
291 self.infos["MODULES"] = module_info_dict
292 self.infos["LISTS"] = list_info_dict
293 self.infos["CONFIGURATIONS"] = configuration_info_dict
294 self.infos["FILES"] = file_dict
296 def loadSourceTree(self):
298 Index BeRTOS source and load it in memory.
300 # Index only the BERTOS_PATH/bertos content
301 bertos_sources_dir = os.path.join(self.info("BERTOS_PATH"), "bertos")
303 if os.path.exists(bertos_sources_dir):
304 for element in os.walk(bertos_sources_dir):
306 file_dict[f] = file_dict.get(f, []) + [element[0]]
307 self.infos["FILE_DICT"] = file_dict
309 def reloadCpuInfo(self):
310 for cpu_info in self.getCpuInfos():
311 if cpu_info["CPU_NAME"] == self.infos["CPU_NAME"]:
312 self.infos["CPU_INFOS"] = cpu_info
314 #-------------------------------------------------------------------------#
316 def createBertosProject(self):
317 # NOTE: Temporary hack.
319 self._editBertosProject()
321 if not self.from_preset:
322 self._newCustomBertosProject()
324 self._newBertosProjectFromPreset()
326 def _newBertosProject(self):
327 for directory in (self.maindir, self.srcdir, self.prjdir, self.cfgdir, self.hwdir):
328 self._createDirectory(directory)
329 # Write the project file
330 self._writeProjectFile(os.path.join(self.maindir, "project.bertos"))
332 self._writeVersionFile(os.path.join(self.maindir, "VERSION"))
333 # Destination makefile
334 self._writeMakefile()
336 self._copySources(self.bertos_maindir, self.srcdir)
337 # Set properly the autoenabled parameters
338 self._setupAutoenabledParameters()
339 # Copy all the configuration files
340 self._writeCfgFiles(self.bertos_srcdir, self.cfgdir)
341 # Destination wizard mk file
342 self._writeWizardMkFile()
344 def _newCustomBertosProject(self):
345 # Create/write/copy the common things
346 self._newBertosProject()
347 # Copy the clean hw files
348 self._createDirectory(self.hwdir)
349 # Copy all the hw files
350 self._writeHwFiles(self.bertos_srcdir, self.hwdir)
351 # Destination user mk file
352 self._writeUserMkFile()
353 # Destination main.c file
354 self._writeMainFile(self.prjdir + "/main.c")
355 # Create project files for selected plugins
356 self._createProjectFiles()
358 def _newBertosProjectFromPreset(self):
359 # Create/write/copy the common things
360 self._newBertosProject()
362 # Copy all the files and dirs except cfg/hw/*.mk
363 self._writeCustomSrcFiles()
366 self._writeAllPresetHwFiles(self.src_hwdir, self.hwdir)
368 # Copyt the new *_user.mk file
369 self._writeUserMkFileFromPreset()
371 # Create project files for selected plugins
372 self._createProjectFiles()
374 def _editBertosProject(self):
375 # Write the project file
376 self._writeProjectFile(os.path.join(self.maindir, "project.bertos"))
377 if not self.is_preset:
378 # Generate this files only if the project isn't a preset
380 self._writeVersionFile(os.path.join(self.maindir, "VERSION"))
381 # Destination makefile
382 self._writeMakefile()
384 self._mergeSources(self.bertos_maindir, self.srcdir, self.old_srcdir)
385 # Copy all the hw files
386 self._writeHwFiles(self.bertos_srcdir, self.hwdir)
387 # Destination wizard mk file
388 self._writeWizardMkFile()
389 # Set properly the autoenabled parameters
390 self._setupAutoenabledParameters()
391 # Copy all the configuration files
392 self._writeCfgFiles(self.bertos_srcdir, self.cfgdir)
393 if not self.is_preset:
394 # Create project files for selected plugins only if the project isn't a preset
395 self._createProjectFiles()
397 def _createProjectFiles(self):
398 # Files for selected plugins
400 for plugin in self.infos["OUTPUT"]:
401 module = loadPlugin(plugin)
402 relevants_files[plugin] = module.createProject(self)
403 self.infos["RELEVANT_FILES"] = relevants_files
405 def _writeVersionFile(self, filename):
406 if not self.edit or self.old_srcdir:
407 version_file = open(os.path.join(const.DATA_DIR, "vtemplates/VERSION"), "r").read()
408 open(filename, "w").write(versionFileGenerator(self, version_file))
410 def _writeProjectFile(self, filename):
411 f = open(filename, "w")
412 f.write(projectFileGenerator(self))
415 def _writeMakefile(self):
416 bertos_utils.makefileGenerator(self)
418 def _writeUserMkFile(self):
419 bertos_utils.userMkGenerator(self)
421 def _writeUserMkFileFromPreset(self):
422 bertos_utils.userMkGeneratorFromPreset(self)
424 def _writeWizardMkFile(self):
425 bertos_utils.mkGenerator(self)
427 def _writeMainFile(self, filename):
428 main = open(os.path.join(const.DATA_DIR, "srctemplates/main.c"), "r").read()
429 open(filename, "w").write(main)
431 def _writeHwFiles(self, source_dir, destination_dir):
432 for module, information in self.infos["MODULES"].items():
433 for hwfile in information["hw"]:
436 string = open(source_dir + "/" + hwfile, "r").read()
437 hwfile_path = destination_dir + "/" + os.path.basename(hwfile)
438 if not self.edit or not os.path.exists(hwfile_path):
439 # If not in editing mode it copies all the hw files. If in
440 # editing mode it copies only the files that don't exist yet
441 open(os.path.join(destination_dir,os.path.basename(hwfile)), "w").write(string)
443 def _writeAllPresetHwFiles(self, source_dir, destination_dir):
445 Copy all but directories contained into the preset hw directory.
446 It's needed because some presets need custom hw files not defined with
447 Wizard directives into modules...
449 source_dir = os.path.join(source_dir, "hw")
450 for f in os.listdir(source_dir):
451 abspath = os.path.join(source_dir, f)
452 if not os.path.isdir(abspath):
453 # Exlude directories from the copy!
454 hw_file = open(os.path.join(source_dir, f), 'r').read()
455 open(os.path.join(destination_dir, f), 'w').write(hw_file)
457 def _writeCfgFiles(self, source_dir, destination_dir):
458 for configuration, information in self.infos["CONFIGURATIONS"].items():
459 string = open(source_dir + "/" + configuration, "r").read()
460 for start, parameter in information["paramlist"]:
461 infos = information[parameter]
462 value = infos["value"]
463 if "unsigned" in infos["informations"] and infos["informations"]["unsigned"]:
465 if "long" in infos["informations"] and infos["informations"]["long"]:
467 string = sub(string, parameter, value)
468 f = open(os.path.join(destination_dir, os.path.basename(configuration)), "w")
472 def _writeCustomSrcFiles(self):
473 origin = self.infos["PRESET_SRC_PATH"]
474 # Files to be ignored (all project files, cfg dir, wizard mk file, all global ignored dirs)
475 project_related_stuff = (
478 self.infos["PRESET_NAME"] + ".mk",
479 self.infos["PRESET_NAME"] + "_user.mk",
481 self.infos["PRESET_NAME"] + ".project",
482 self.infos["PRESET_NAME"] + ".workspace",
483 ) + const.IGNORE_LIST
484 for element in os.listdir(origin):
485 if element not in project_related_stuff:
486 full_path = os.path.join(origin, element)
487 if os.path.isdir(full_path):
488 copytree.copytree(full_path, os.path.join(self.prjdir, element), ignore_list=const.IGNORE_LIST)
490 shutil.copy(full_path, self.prjdir)
492 def _setupAutoenabledParameters(self):
493 for module, information in self.infos["MODULES"].items():
494 if "configuration" in information and information["configuration"] != "":
495 configurations = self.infos["CONFIGURATIONS"]
496 configuration = configurations[information["configuration"]]
497 for start, parameter in configuration["paramlist"]:
498 if "type" in configuration[parameter]["informations"] and configuration[parameter]["informations"]["type"] == "autoenabled":
499 configuration[parameter]["value"] = "1" if information["enabled"] else "0"
500 self.infos["CONFIGURATIONS"] = configurations
502 # Project related properties
505 return self.infos.get("PROJECT_PATH", None)
510 return os.path.join(self.maindir, "bertos")
516 return self.infos.get("PROJECT_SRC_PATH", None)
521 return os.path.join(self.prjdir, "hw")
528 return os.path.join(self.prjdir, "cfg")
533 def old_srcdir(self):
534 return self.infos.get("OLD_BERTOS_PATH", None)
536 # BeRTOS sources related properties
538 def bertos_maindir(self):
539 return self.infos.get("BERTOS_PATH", None)
542 def bertos_srcdir(self):
543 if self.bertos_maindir:
544 return os.path.join(self.bertos_maindir, "bertos")
551 return os.path.join(self.infos["PRESET_PATH"], self.infos["PRESET_HW_PATH"])
553 return self.bertos_maindir
556 def from_preset(self):
557 return self.infos.get("PROJECT_FROM_PRESET", False)
561 return self.infos.get("PRESET", False)
563 def _createDirectory(self, directory):
566 if os.path.isdir(directory):
567 shutil.rmtree(directory, True)
568 os.makedirs(directory)
570 def _copySources(self, origin, destination):
571 # If not in editing mode it copies all the bertos sources in the /bertos subdirectory of the project
572 shutil.rmtree(destination, True)
573 copytree.copytree(origin + "/bertos", destination, ignore_list=const.IGNORE_LIST)
575 def _mergeSources(self, origin, destination, old_sources_dir):
577 mergeSources(destination, origin, old_sources_dir)
579 def setInfo(self, key, value):
581 Store the given value with the name key.
583 self.infos[key] = value
585 def info(self, key, default=None):
587 Retrieve the value associated with the name key.
589 if key in self.infos:
590 return copy.deepcopy(self.infos[key])
593 def getCpuInfos(self):
595 for definition in self.findDefinitions(const.CPU_DEFINITION):
596 cpuInfos.append(getInfos(definition))
599 def searchFiles(self, filename):
600 file_dict = self.infos["FILE_DICT"]
601 return [(filename, dirname) for dirname in file_dict.get(filename, [])]
603 def findDefinitions(self, ftype):
604 # Maintain a cache for every scanned BERTOS_PATH
605 definitions_dict = self._cached_queries.get(self.infos["BERTOS_PATH"], {})
606 definitions = definitions_dict.get(ftype, None)
607 if definitions is not None:
609 file_dict = self.infos["FILE_DICT"]
611 for filename in file_dict:
612 if fnmatch.fnmatch(filename, ftype):
613 definitions += [(filename, dirname) for dirname in file_dict.get(filename, [])]
615 # If no cache for the current BERTOS_PATH create an empty one
616 if not definitions_dict:
617 self._cached_queries[self.infos["BERTOS_PATH"]] = {}
618 # Fill the empty cache with the result
619 self._cached_queries[self.infos["BERTOS_PATH"]][ftype] = definitions
622 def setEnabledModules(self, enabled_modules):
623 modules = self.infos["MODULES"]
625 for module, information in modules.items():
626 information["enabled"] = module in enabled_modules
627 if information["enabled"]:
628 for dependency in information["depends"]:
629 if not dependency in modules:
630 files[dependency] = files.get(dependency, 0) + 1
631 self.infos["MODULES"] = modules
632 self.infos["FILES"] = files
635 return "<BProject:instance %d>%s" %(id(self), repr(self.infos))