Refactor to use new protocol module and sipo.
[bertos.git] / wizard / BProject.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 #
32 # Author: Lorenzo Berni <duplo@develer.com>
33 #
34
35 import os
36 import fnmatch
37 import copy
38 import pickle
39 import shutil
40 import copytree
41
42 import DefineException
43
44 from LoadException import VersionException, ToolchainException
45
46 import const
47
48 from bertos_utils import (
49                             # Utility functions
50                             isBertosDir, getTagSet, getInfos, updateConfigurationValues,
51                             loadConfigurationInfos, loadDefineLists, loadModuleDefinition,
52                             getCommentList, sub,
53
54                             # Project creation functions
55                             projectFileGenerator, versionFileGenerator, loadPlugin,
56                             mergeSources,
57
58                             # Custom exceptions
59                             ParseError, SupportedException
60                         )
61 import bertos_utils
62
63 from compatibility import updateProject
64
65 class BProject(object):
66     """
67     Simple class for store and retrieve project informations.
68     """
69
70     def __init__(self, project_file="", info_dict={}):
71         self.infos = {}
72         self._cached_queries = {}
73         self.edit = False
74         if project_file:
75             self.edit = True
76             self.loadBertosProject(project_file, info_dict)
77
78     #--- Load methods (methods that loads data into project) ------------------#
79
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"]))
89         if project_src_path:
90             self.infos["PROJECT_SRC_PATH"] = project_src_path
91
92         else:
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"]))
96
97         self.infos["PROJECT_SRC_PATH_FROM_MAKEFILE"] = project_data.get("PROJECT_SRC_PATH_FROM_MAKEFILE")
98         self.infos["PROJECT_HW_PATH_FROM_MAKEFILE"] = project_data.get("PROJECT_HW_PATH_FROM_MAKEFILE")
99
100         linked_sources_path = project_data["BERTOS_PATH"]
101         sources_abspath = os.path.abspath(os.path.join(project_dir, linked_sources_path))
102         project_data["BERTOS_PATH"] = sources_abspath
103
104         self._loadBertosSourceStuff(project_data["BERTOS_PATH"], info_dict.get("BERTOS_PATH", None))
105
106         self.infos["PRESET"] = project_data.get("PRESET", False)
107
108         # For those projects that don't have a VERSION file create a dummy one.
109         if not isBertosDir(project_dir) and not self.is_preset:
110             version_file = open(os.path.join(const.DATA_DIR, "vtemplates/VERSION"), "r").read()
111             open(os.path.join(project_dir, "VERSION"), "w").write(version_file.replace("$version", "").strip())
112
113         self.loadSourceTree()
114         self._loadCpuStuff(project_data["CPU_NAME"], project_data["SELECTED_FREQ"])
115         self._loadToolchainStuff(project_data["TOOLCHAIN"], info_dict.get("TOOLCHAIN", None))
116         self.infos["OUTPUT"] = project_data["OUTPUT"]
117         self.loadModuleData(True)
118         self.setEnabledModules(project_data["ENABLED_MODULES"])
119
120     def _loadBertosSourceStuff(self, sources_path, forced_version=None):
121         if forced_version:
122             sources_path = forced_version
123         if os.path.exists(sources_path):
124             self.infos["BERTOS_PATH"] = sources_path
125         else:
126             raise VersionException(self)
127
128     def _loadCpuStuff(self, cpu_name, cpu_frequency):
129         self.infos["CPU_NAME"] = cpu_name
130         cpu_info = self.getCpuInfos()
131         for cpu in cpu_info:
132             if cpu["CPU_NAME"] == cpu_name:
133                 self.infos["CPU_INFOS"] = cpu
134                 break
135         tag_list = getTagSet(cpu_info)
136         # Create, fill and store the dict with the tags
137         tag_dict = {}
138         for element in tag_list:
139             tag_dict[element] = False
140         infos = self.info("CPU_INFOS")
141         for tag in tag_dict:
142             if tag in infos["CPU_TAGS"] + [infos["CPU_NAME"], infos["TOOLCHAIN"]]:
143                 tag_dict[tag] = True
144             else:
145                 tag_dict[tag] = False
146         self.infos["ALL_CPU_TAGS"] = tag_dict
147         self.infos["SELECTED_FREQ"] = cpu_frequency
148
149     def _loadToolchainStuff(self, toolchain, forced_toolchain=None):
150         toolchain = toolchain
151         if forced_toolchain:
152             toolchain = forced_toolchain
153         if os.path.exists(toolchain["path"]) or bertos_utils.findInPath(toolchain["path"]):
154             self.infos["TOOLCHAIN"] = toolchain
155         else:
156             raise ToolchainException(self)
157
158     def loadProjectFromPreset(self, preset):
159         """
160         Load a project from a preset.
161         NOTE: this is a stub.
162         """
163         project_file = os.path.join(preset, "project.bertos")
164         project_data = pickle.loads(open(project_file, "r").read())
165         self.loadSourceTree()
166         self._loadCpuStuff(project_data["CPU_NAME"], project_data["SELECTED_FREQ"])
167
168         # NOTE: this is a HACK!!!
169         # TODO: find a better way to reuse loadModuleData
170         preset_project_name = project_data.get("PROJECT_NAME", os.path.basename(preset))
171         preset_prj_src_path = os.path.join(preset, project_data.get("PROJECT_SRC_PATH", os.path.join(preset, preset_project_name)))
172         preset_prj_hw_path = os.path.join(preset, project_data.get("PROJECT_HW_PATH", preset))
173
174         old_project_name = self.infos["PROJECT_NAME"]
175         old_project_path = self.infos["PROJECT_PATH"]
176         old_project_src_path = self.infos["PROJECT_SRC_PATH"]
177         old_project_hw_path = self.infos["PROJECT_HW_PATH"]
178
179         self.infos["PROJECT_NAME"] = preset_project_name
180         self.infos["PROJECT_PATH"] = preset
181         self.infos["PROJECT_SRC_PATH"] = preset_prj_src_path
182         self.infos["PROJECT_HW_PATH"] = preset_prj_hw_path
183
184         self.loadModuleData(True)
185         self.setEnabledModules(project_data["ENABLED_MODULES"])
186
187         self.infos["PROJECT_NAME"] = old_project_name
188         self.infos["PROJECT_PATH"] = old_project_path
189         self.infos["PROJECT_SRC_PATH"] = old_project_src_path
190         self.infos["PROJECT_HW_PATH"] = old_project_hw_path
191         # End of the ugly HACK!
192
193         self.infos["PRESET_NAME"] = preset_project_name
194         self.infos["PRESET_PATH"] = preset
195         self.infos["PRESET_SRC_PATH"] = preset_prj_src_path
196         self.infos["PRESET_HW_PATH"] = preset_prj_hw_path
197
198     def loadProjectPresets(self):
199         """
200         Load the default presets (into the const.PREDEFINED_BOARDS_DIR).
201         """
202         # NOTE: this method does nothing (for now).
203         preset_path = os.path.join(self.infos["BERTOS_PATH"], const.PREDEFINED_BOARDS_DIR)
204         preset_tree = {"children": []}
205         if os.path.exists(preset_path):
206             preset_tree = self._loadProjectPresetTree(preset_path)
207         self.infos["PRESET_TREE"] = preset_tree
208
209     def _loadProjectPresetTree(self, path):
210         _tree = {}
211         _tree["info"] = self._loadPresetInfo(os.path.join(path, const.PREDEFINED_BOARD_SPEC_FILE))
212         _tree["info"]["filename"] = os.path.basename(path)
213         _tree["info"]["path"] = path
214         _tree["children"] = {}
215         entries = set(os.listdir(path))
216         for entry in entries:
217             _path = os.path.join(path, entry)
218             if os.path.isdir(_path):
219                 sub_entries = set(os.listdir(_path))
220                 if const.PREDEFINED_BOARD_SPEC_FILE in sub_entries:
221                     _tree["children"][_path] = self._loadProjectPresetTree(_path)
222         # Add into the info dict the dir type (dir/project)
223         if _tree["children"]:
224             _tree["info"]["type"] = "dir"
225         else:
226             _tree["info"]["type"] = "project"
227         return _tree
228
229     def _loadPresetInfo(self, preset_spec_file):
230         D = {}
231         try:
232             execfile(preset_spec_file, {}, D)
233         except IOError, e:
234             pass
235         return D
236
237     def loadModuleData(self, edit=False):
238         module_info_dict = {}
239         list_info_dict = {}
240         configuration_info_dict = {}
241         file_dict = {}
242         for filename, path in self.findDefinitions("*.h") + self.findDefinitions("*.c") + self.findDefinitions("*.s") + self.findDefinitions("*.S"):
243             comment_list = getCommentList(open(path + "/" + filename, "r").read())
244             if len(comment_list) > 0:
245                 module_info = {}
246                 configuration_info = {}
247                 try:
248                     to_be_parsed, module_dict = loadModuleDefinition(comment_list[0])
249                 except ParseError, err:
250                     raise DefineException.ModuleDefineException(os.path.join(path, filename), err.line_number, err.line)
251                 for module, information in module_dict.items():
252                     if "depends" not in information:
253                         information["depends"] = ()
254                     information["depends"] += (filename.split(".")[0],)
255                     information["category"] = os.path.basename(path)
256
257                     # Hack to remove 'bertos/' from the configuration file path.
258                     #
259                     # The new module information format substitute paths like 'bertos/cfg/config_file.h'
260                     # with the relative path into the bertos directory ('cfg/config_file.h')
261                     information["configuration"] = information["configuration"].replace("bertos/", "")
262                     information["hw"] = [hw.replace("bertos/", "") for hw in information["hw"]]
263
264                     if "configuration" in information and len(information["configuration"]):
265                         configuration = module_dict[module]["configuration"]
266                         try:
267                             cfg_file_path = os.path.join(self.bertos_srcdir, configuration)
268                             configuration_info[configuration] = loadConfigurationInfos(cfg_file_path)
269                         except ParseError, err:
270                             raise DefineException.ConfigurationDefineException(cfg_file_path, err.line_number, err.line)
271                         if edit:
272                             try:
273                                 path = self.infos["PROJECT_SRC_PATH"]
274                                 cfg_file_path = os.path.join(path, configuration)
275                                 configuration_info[configuration] = updateConfigurationValues(configuration_info[configuration], loadConfigurationInfos(cfg_file_path))
276                             except ParseError, err:
277                                 raise DefineException.ConfigurationDefineException(cfg_file_path, err.line_number, err.line)
278                             except IOError, err:
279                                 # The wizard can't find the file, use the default configuration
280                                 pass
281                 module_info_dict.update(module_dict)
282                 configuration_info_dict.update(configuration_info)
283                 if to_be_parsed:
284                     try:
285                         list_dict = loadDefineLists(comment_list[1:])
286                         list_info_dict.update(list_dict)
287                     except ParseError, err:
288                         raise DefineException.EnumDefineException(os.path.join(path, filename), err.line_number, err.line)
289         for tag in self.infos["CPU_INFOS"]["CPU_TAGS"]:
290             for filename, path in self.findDefinitions("*_" + tag + ".h"):
291                 comment_list = getCommentList(open(path + "/" + filename, "r").read())
292                 list_info_dict.update(loadDefineLists(comment_list))
293         self.infos["MODULES"] = module_info_dict
294         self.infos["LISTS"] = list_info_dict
295         self.infos["CONFIGURATIONS"] = configuration_info_dict
296         self.infos["FILES"] = file_dict
297
298     def loadSourceTree(self):
299         """
300         Index BeRTOS source and load it in memory.
301         """
302         # Index only the BERTOS_PATH/bertos content
303         bertos_sources_dir = os.path.join(self.info("BERTOS_PATH"), "bertos")
304         file_dict = {}
305         if os.path.exists(bertos_sources_dir):
306             for element in os.walk(bertos_sources_dir):
307                 for f in element[2]:
308                     file_dict[f] = file_dict.get(f, []) + [element[0]]
309         self.infos["FILE_DICT"] = file_dict
310
311     def reloadCpuInfo(self):
312         for cpu_info in self.getCpuInfos():
313             if cpu_info["CPU_NAME"] == self.infos["CPU_NAME"]:
314                 self.infos["CPU_INFOS"] = cpu_info
315
316     #-------------------------------------------------------------------------#
317
318     def createBertosProject(self):
319         # NOTE: Temporary hack.
320         if self.edit:
321             self._editBertosProject()
322         else:
323             if not self.from_preset:
324                 self._newCustomBertosProject()
325             else:
326                 self._newBertosProjectFromPreset()
327
328     def _newBertosProject(self):
329         for directory in (self.maindir, self.srcdir, self.prjdir, self.cfgdir, self.hwdir):
330             self._createDirectory(directory)
331         # Write the project file
332         self._writeProjectFile(os.path.join(self.maindir, "project.bertos"))
333         # VERSION file
334         self._writeVersionFile(os.path.join(self.maindir, "VERSION"))
335         # Destination makefile
336         self._writeMakefile()
337         # Copy the sources
338         self._copySources(self.bertos_maindir, self.srcdir)
339         # Set properly the autoenabled parameters
340         self._setupAutoenabledParameters()
341         # Copy all the configuration files
342         self._writeCfgFiles(self.bertos_srcdir, self.cfgdir)
343         # Destination wizard mk file
344         self._writeWizardMkFile()
345
346     def _newCustomBertosProject(self):
347         # Create/write/copy the common things
348         self._newBertosProject()
349         # Copy the clean hw files
350         self._createDirectory(self.hwdir)
351         # Copy all the hw files
352         self._writeHwFiles(self.bertos_srcdir, self.hwdir)
353         # Destination user mk file
354         self._writeUserMkFile()
355         # Destination main.c file
356         self._writeMainFile(self.prjdir + "/main.c")
357         # Create project files for selected plugins
358         self._createProjectFiles()
359
360     def _newBertosProjectFromPreset(self):
361         # Create/write/copy the common things
362         self._newBertosProject()
363
364         # Copy all the files and dirs except cfg/hw/*.mk
365         self._writeCustomSrcFiles()
366
367         # Copy the hw files
368         self._writeAllPresetHwFiles(self.src_hwdir, self.hwdir)
369
370         # Copyt the new *_user.mk file
371         self._writeUserMkFileFromPreset()
372
373         # Create project files for selected plugins
374         self._createProjectFiles()
375
376     def _editBertosProject(self):
377         # Write the project file
378         self._writeProjectFile(os.path.join(self.maindir, "project.bertos"))
379         if not self.is_preset:
380             # Generate this files only if the project isn't a preset
381             # VERSION file
382             self._writeVersionFile(os.path.join(self.maindir, "VERSION"))
383             # Destination makefile
384             self._writeMakefile()
385             # Merge sources
386             self._mergeSources(self.bertos_maindir, self.srcdir, self.old_srcdir)
387             # Copy all the hw files
388             self._writeHwFiles(self.bertos_srcdir, self.hwdir)
389
390         # Destination wizard mk file (it seems that this file need to be
391         # rewritten also if the project is a preset)...
392         self._writeWizardMkFile()
393
394         # Set properly the autoenabled parameters
395         self._setupAutoenabledParameters()
396         # Copy all the configuration files
397         self._writeCfgFiles(self.bertos_srcdir, self.cfgdir)
398         if not self.is_preset:
399             # Create project files for selected plugins only if the project isn't a preset
400             self._createProjectFiles()
401
402     def _createProjectFiles(self):
403         # Files for selected plugins
404         relevants_files = {}
405         for plugin in self.infos["OUTPUT"]:
406             module = loadPlugin(plugin)
407             relevants_files[plugin] = module.createProject(self)
408         self.infos["RELEVANT_FILES"] = relevants_files
409
410     def _writeVersionFile(self, filename):
411         if not self.edit or self.old_srcdir:
412             version_file = open(os.path.join(const.DATA_DIR, "vtemplates/VERSION"), "r").read()
413             open(filename, "w").write(versionFileGenerator(self, version_file))
414
415     def _writeProjectFile(self, filename):
416         f = open(filename, "w")
417         f.write(projectFileGenerator(self))
418         f.close()
419
420     def _writeMakefile(self):
421         bertos_utils.makefileGenerator(self)
422
423     def _writeUserMkFile(self):
424         bertos_utils.userMkGenerator(self)
425
426     def _writeUserMkFileFromPreset(self):
427         bertos_utils.userMkGeneratorFromPreset(self)
428
429     def _writeWizardMkFile(self):
430         bertos_utils.mkGenerator(self)
431
432     def _writeMainFile(self, filename):
433         main = open(os.path.join(const.DATA_DIR, "srctemplates/main.c"), "r").read()
434         open(filename, "w").write(main)
435
436     def _writeHwFiles(self, source_dir, destination_dir):
437         for module, information in self.infos["MODULES"].items():
438             for hwfile in information["hw"]:
439                 if hwfile == "":
440                     continue
441                 string = open(source_dir + "/" + hwfile, "r").read()
442                 hwfile_path = destination_dir + "/" + os.path.basename(hwfile)
443                 if not self.edit or not os.path.exists(hwfile_path):
444                     # If not in editing mode it copies all the hw files. If in
445                     # editing mode it copies only the files that don't exist yet
446                     open(os.path.join(destination_dir,os.path.basename(hwfile)), "w").write(string)
447
448     def _writeAllPresetHwFiles(self, source_dir, destination_dir):
449         """
450         Copy all but directories contained into the preset hw directory.
451         It's needed because some presets need custom hw files not defined with
452         Wizard directives into modules...
453         """
454         source_dir = os.path.join(source_dir, "hw")
455         for f in os.listdir(source_dir):
456             abspath = os.path.join(source_dir, f)
457             if not os.path.isdir(abspath):
458                 # Exlude directories from the copy!
459                 hw_file = open(os.path.join(source_dir, f), 'r').read()
460                 open(os.path.join(destination_dir, f), 'w').write(hw_file)
461
462     def _writeCfgFiles(self, source_dir, destination_dir):
463         for configuration, information in self.infos["CONFIGURATIONS"].items():
464             string = open(source_dir + "/" + configuration, "r").read()
465             for start, parameter in information["paramlist"]:
466                 infos = information[parameter]
467                 value = infos["value"]
468                 if "unsigned" in infos["informations"] and infos["informations"]["unsigned"]:
469                     value += "U"
470                 if "long" in infos["informations"] and infos["informations"]["long"]:
471                     value += "L"
472                 string = sub(string, parameter, value)
473             f = open(os.path.join(destination_dir, os.path.basename(configuration)), "w")
474             f.write(string)
475             f.close()
476
477     def _writeCustomSrcFiles(self):
478         origin = self.infos["PRESET_SRC_PATH"]
479         # Files to be ignored (all project files, cfg dir, wizard mk file, all global ignored dirs)
480         project_related_stuff = (
481             "cfg",
482             "hw",
483             self.infos["PRESET_NAME"] + ".mk",
484             self.infos["PRESET_NAME"] + "_user.mk",
485             "project.bertos",
486             self.infos["PRESET_NAME"] + ".project",
487             self.infos["PRESET_NAME"] + ".workspace",
488         ) + const.IGNORE_LIST
489         for element in os.listdir(origin):
490             if element not in project_related_stuff:
491                 full_path = os.path.join(origin, element)
492                 if os.path.isdir(full_path):
493                     copytree.copytree(full_path, os.path.join(self.prjdir, element), ignore_list=const.IGNORE_LIST)
494                 else:
495                     shutil.copy(full_path, self.prjdir)
496
497     def _setupAutoenabledParameters(self):
498         for module, information in self.infos["MODULES"].items():
499             if "configuration" in information and information["configuration"] != "":
500                 configurations = self.infos["CONFIGURATIONS"]
501                 configuration = configurations[information["configuration"]]
502                 for start, parameter in configuration["paramlist"]:
503                     if "type" in configuration[parameter]["informations"] and configuration[parameter]["informations"]["type"] == "autoenabled":
504                         configuration[parameter]["value"] = "1" if information["enabled"] else "0"
505                 self.infos["CONFIGURATIONS"] = configurations
506
507     # Project related properties
508     @property
509     def maindir(self):
510         return self.infos.get("PROJECT_PATH", None)
511
512     @property
513     def srcdir(self):
514         if self.maindir:
515             return os.path.join(self.maindir, "bertos")
516         else:
517             return None
518
519     @property
520     def prjdir(self):
521         return self.infos.get("PROJECT_SRC_PATH", None)
522
523     @property
524     def hwdir(self):
525         if self.prjdir:
526             return os.path.join(self.prjdir, "hw")
527         else:
528             return None
529
530     @property
531     def cfgdir(self):
532         if self.prjdir:
533             return os.path.join(self.prjdir, "cfg")
534         else:
535             return None
536
537     @property
538     def old_srcdir(self):
539         return self.infos.get("OLD_BERTOS_PATH", None)
540
541     # BeRTOS sources related properties
542     @property
543     def bertos_maindir(self):
544         return self.infos.get("BERTOS_PATH", None)
545
546     @property
547     def bertos_srcdir(self):
548         if self.bertos_maindir:
549             return os.path.join(self.bertos_maindir, "bertos")
550         else:
551             return None
552
553     @property
554     def src_hwdir(self):
555         if self.from_preset:
556             return os.path.join(self.infos["PRESET_PATH"], self.infos["PRESET_HW_PATH"])
557         else:
558             return self.bertos_maindir
559
560     @property
561     def from_preset(self):
562         return self.infos.get("PROJECT_FROM_PRESET", False)
563
564     @property
565     def is_preset(self):
566         return self.infos.get("PRESET", False)
567
568     def _createDirectory(self, directory):
569         if not directory:
570             return
571         if os.path.isdir(directory):
572             shutil.rmtree(directory, True)
573         os.makedirs(directory)
574
575     def _copySources(self, origin, destination):
576         # If not in editing mode it copies all the bertos sources in the /bertos subdirectory of the project
577         shutil.rmtree(destination, True)
578         copytree.copytree(origin + "/bertos", destination, ignore_list=const.IGNORE_LIST)
579
580     def _mergeSources(self, origin, destination, old_sources_dir):
581         if old_sources_dir:
582             mergeSources(destination, origin, old_sources_dir)
583
584     def setInfo(self, key, value):
585         """
586         Store the given value with the name key.
587         """
588         self.infos[key] = value
589
590     def info(self, key, default=None):
591         """
592         Retrieve the value associated with the name key.
593         """
594         if key in self.infos:
595             return copy.deepcopy(self.infos[key])
596         return default
597
598     def getCpuInfos(self):
599         cpuInfos = []
600         for definition in self.findDefinitions(const.CPU_DEFINITION):
601             cpuInfos.append(getInfos(definition))
602         return cpuInfos
603
604     def searchFiles(self, filename):
605         file_dict = self.infos["FILE_DICT"]
606         return [(filename, dirname) for dirname in file_dict.get(filename, [])]
607
608     def findDefinitions(self, ftype):
609         # Maintain a cache for every scanned BERTOS_PATH
610         definitions_dict = self._cached_queries.get(self.infos["BERTOS_PATH"], {})
611         definitions = definitions_dict.get(ftype, None)
612         if definitions is not None:
613             return definitions
614         file_dict = self.infos["FILE_DICT"]
615         definitions = []
616         for filename in file_dict:
617             if fnmatch.fnmatch(filename, ftype):
618                 definitions += [(filename, dirname) for dirname in file_dict.get(filename, [])]
619
620         # If no cache for the current BERTOS_PATH create an empty one
621         if not definitions_dict:
622             self._cached_queries[self.infos["BERTOS_PATH"]] = {}
623         # Fill the empty cache with the result
624         self._cached_queries[self.infos["BERTOS_PATH"]][ftype] = definitions
625         return definitions
626
627     def setEnabledModules(self, enabled_modules):
628         modules = self.infos["MODULES"]
629         files = {}
630         for module, information in modules.items():
631             information["enabled"] = module in enabled_modules
632             if information["enabled"]:
633                 for dependency in information["depends"]:
634                     if not dependency in modules:
635                         files[dependency] = files.get(dependency, 0) + 1
636         self.infos["MODULES"] = modules
637         self.infos["FILES"] = files
638
639     def __repr__(self):
640         return "<BProject:instance %d>%s" %(id(self), repr(self.infos))