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