Fix regression.
[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         self._loadToolchainStuff(project_data["TOOLCHAIN"])
166
167         # NOTE: this is a HACK!!!
168         # TODO: find a better way to reuse loadModuleData
169         preset_project_name = project_data.get("PROJECT_NAME", os.path.basename(preset))
170         preset_prj_src_path = os.path.join(preset, project_data.get("PROJECT_SRC_PATH", os.path.join(preset, preset_project_name)))
171         preset_prj_hw_path = os.path.join(preset, project_data.get("PROJECT_HW_PATH", preset))
172
173         old_project_name = self.infos["PROJECT_NAME"]
174         old_project_path = self.infos["PROJECT_PATH"]
175         old_project_src_path = self.infos["PROJECT_SRC_PATH"]
176         old_project_hw_path = self.infos["PROJECT_HW_PATH"]
177
178         self.infos["PROJECT_NAME"] = preset_project_name
179         self.infos["PROJECT_PATH"] = preset
180         self.infos["PROJECT_SRC_PATH"] = preset_prj_src_path
181         self.infos["PROJECT_HW_PATH"] = preset_prj_hw_path
182
183         self.loadModuleData(True)
184         self.setEnabledModules(project_data["ENABLED_MODULES"])
185
186         self.infos["PROJECT_NAME"] = old_project_name
187         self.infos["PROJECT_PATH"] = old_project_path
188         self.infos["PROJECT_SRC_PATH"] = old_project_src_path
189         self.infos["PROJECT_HW_PATH"] = old_project_hw_path
190         # End of the ugly HACK!
191
192         self.infos["PRESET_NAME"] = preset_project_name
193         self.infos["PRESET_PATH"] = preset
194         self.infos["PRESET_SRC_PATH"] = preset_prj_src_path
195         self.infos["PRESET_HW_PATH"] = preset_prj_hw_path
196
197     def loadProjectPresets(self):
198         """
199         Load the default presets (into the const.PREDEFINED_BOARDS_DIR).
200         """
201         # NOTE: this method does nothing (for now).
202         preset_path = os.path.join(self.infos["BERTOS_PATH"], const.PREDEFINED_BOARDS_DIR)
203         preset_tree = {}
204         if os.path.exists(preset_path):
205             preset_tree = self._loadProjectPresetTree(preset_path)
206         self.infos["PRESET_TREE"] = preset_tree
207
208     def _loadProjectPresetTree(self, path):
209         _tree = {}
210         _tree["info"] = self._loadPresetInfo(os.path.join(path, const.PREDEFINED_BOARD_SPEC_FILE))
211         _tree["info"]["filename"] = os.path.basename(path)
212         _tree["info"]["path"] = path
213         _tree["children"] = []
214         entries = set(os.listdir(path))
215         for entry in entries:
216             _path = os.path.join(path, entry)
217             if os.path.isdir(_path):
218                 sub_entries = set(os.listdir(_path))
219                 if const.PREDEFINED_BOARD_SPEC_FILE in sub_entries:
220                     _tree["children"].append(self._loadProjectPresetTree(_path))
221         # Add into the info dict the dir type (dir/project)
222         if _tree["children"]:
223             _tree["info"]["type"] = "dir"
224         else:
225             _tree["info"]["type"] = "project"
226         return _tree
227
228     def _loadPresetInfo(self, preset_spec_file):
229         D = {}
230         execfile(preset_spec_file, {}, D)
231         return D
232
233     def loadModuleData(self, edit=False):
234         module_info_dict = {}
235         list_info_dict = {}
236         configuration_info_dict = {}
237         file_dict = {}
238         for filename, path in self.findDefinitions("*.h") + self.findDefinitions("*.c") + self.findDefinitions("*.s") + self.findDefinitions("*.S"):
239             comment_list = getCommentList(open(path + "/" + filename, "r").read())
240             if len(comment_list) > 0:
241                 module_info = {}
242                 configuration_info = {}
243                 try:
244                     to_be_parsed, module_dict = loadModuleDefinition(comment_list[0])
245                 except ParseError, err:
246                     raise DefineException.ModuleDefineException(path, err.line_number, err.line)
247                 for module, information in module_dict.items():
248                     if "depends" not in information:
249                         information["depends"] = ()
250                     information["depends"] += (filename.split(".")[0],)
251                     information["category"] = os.path.basename(path)
252                     
253                     # Hack to remove 'bertos/' from the configuration file path.
254                     #
255                     # The new module information format substitute paths like 'bertos/cfg/config_file.h'
256                     # with the relative path into the bertos directory ('cfg/config_file.h')
257                     information["configuration"] = information["configuration"].replace("bertos/", "")
258                     information["hw"] = [hw.replace("bertos/", "") for hw in information["hw"]]
259
260                     if "configuration" in information and len(information["configuration"]):
261                         configuration = module_dict[module]["configuration"]
262                         try:
263                             cfg_file_path = os.path.join(self.bertos_srcdir, configuration)
264                             configuration_info[configuration] = loadConfigurationInfos(cfg_file_path)
265                         except ParseError, err:
266                             raise DefineException.ConfigurationDefineException(cfg_file_path, err.line_number, err.line)
267                         if edit:
268                             try:
269                                 path = self.infos["PROJECT_SRC_PATH"]
270                                 cfg_file_path = os.path.join(path, configuration)
271                                 configuration_info[configuration] = updateConfigurationValues(configuration_info[configuration], loadConfigurationInfos(cfg_file_path))
272                             except ParseError, err:
273                                 raise DefineException.ConfigurationDefineException(cfg_file_path, err.line_number, err.line)
274                 module_info_dict.update(module_dict)
275                 configuration_info_dict.update(configuration_info)
276                 if to_be_parsed:
277                     try:
278                         list_dict = loadDefineLists(comment_list[1:])
279                         list_info_dict.update(list_dict)
280                     except ParseError, err:
281                         raise DefineException.EnumDefineException(path, err.line_number, err.line)
282         for tag in self.infos["CPU_INFOS"]["CPU_TAGS"]:
283             for filename, path in self.findDefinitions("*_" + tag + ".h"):
284                 comment_list = getCommentList(open(path + "/" + filename, "r").read())
285                 list_info_dict.update(loadDefineLists(comment_list))
286         self.infos["MODULES"] = module_info_dict
287         self.infos["LISTS"] = list_info_dict
288         self.infos["CONFIGURATIONS"] = configuration_info_dict
289         self.infos["FILES"] = file_dict
290
291     def loadSourceTree(self):
292         """
293         Index BeRTOS source and load it in memory.
294         """
295         # Index only the BERTOS_PATH/bertos content
296         bertos_sources_dir = os.path.join(self.info("BERTOS_PATH"), "bertos")
297         file_dict = {}
298         if os.path.exists(bertos_sources_dir):
299             for element in os.walk(bertos_sources_dir):
300                 for f in element[2]:
301                     file_dict[f] = file_dict.get(f, []) + [element[0]]
302         self.infos["FILE_DICT"] = file_dict
303
304     def reloadCpuInfo(self):
305         for cpu_info in self.getCpuInfos():
306             if cpu_info["CPU_NAME"] == self.infos["CPU_NAME"]:
307                 self.infos["CPU_INFOS"] = cpu_info
308
309     #-------------------------------------------------------------------------#
310
311     def createBertosProject(self):
312         # NOTE: Temporary hack.
313         if self.edit:
314             self._editBertosProject()
315         else:
316             if not self.from_preset:
317                 self._newCustomBertosProject()
318             else:
319                 self._newBertosProjectFromPreset()
320
321     def _newBertosProject(self):
322         for directory in (self.maindir, self.srcdir, self.prjdir, self.cfgdir, self.hwdir):
323             self._createDirectory(directory)
324         # Write the project file
325         self._writeProjectFile(os.path.join(self.maindir, "project.bertos"))
326         # VERSION file
327         self._writeVersionFile(os.path.join(self.maindir, "VERSION"))
328         # Destination makefile
329         self._writeMakefile()
330         # Copy the sources
331         self._copySources(self.bertos_maindir, self.srcdir)
332         # Set properly the autoenabled parameters
333         self._setupAutoenabledParameters()
334         # Copy all the configuration files
335         self._writeCfgFiles(self.bertos_srcdir, self.cfgdir)
336         # Destination wizard mk file
337         self._writeWizardMkFile()
338
339     def _newCustomBertosProject(self):
340         # Create/write/copy the common things
341         self._newBertosProject()
342         # Copy the clean hw files
343         self._createDirectory(self.hwdir)
344         # Copy all the hw files
345         self._writeHwFiles(self.bertos_srcdir, self.hwdir)
346         # Destination user mk file
347         self._writeUserMkFile()
348         # Destination main.c file
349         self._writeMainFile(self.prjdir + "/main.c")
350         # Create project files for selected plugins
351         self._createProjectFiles()
352
353     def _newBertosProjectFromPreset(self):
354         # Create/write/copy the common things
355         self._newBertosProject()
356
357         # Copy all the files and dirs except cfg/hw/*.mk
358         self._writeCustomSrcFiles()
359         
360         # Copy the hw files
361         self._writeHwFiles(self.src_hwdir, self.hwdir)
362
363         # Copyt the new *_user.mk file
364         self._writeUserMkFileFromPreset()
365
366         if self.infos["EMPTY_MAIN"]:
367             # Create and empty main.c file only if the user check the box
368             self._writeMainFile(self.prjdir + "/main.c")
369
370         # Create project files for selected plugins
371         self._createProjectFiles()
372
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
378             # VERSION file
379             self._writeVersionFile(os.path.join(self.maindir, "VERSION"))
380             # Destination makefile
381             self._writeMakefile()
382             # Merge sources
383             self._mergeSources(self.bertos_maindir, self.srcdir, self.old_srcdir)
384             # Copy all the hw files
385             self._writeHwFiles(self.bertos_srcdir, self.hwdir)
386             # Destination wizard mk file
387             self._writeWizardMkFile()
388         # Set properly the autoenabled parameters
389         self._setupAutoenabledParameters()
390         # Copy all the configuration files
391         self._writeCfgFiles(self.bertos_srcdir, self.cfgdir)
392         if not self.is_preset:
393             # Create project files for selected plugins only if the project isn't a preset
394             self._createProjectFiles()
395
396     def _createProjectFiles(self):
397         # Files for selected plugins
398         relevants_files = {}
399         for plugin in self.infos["OUTPUT"]:
400             module = loadPlugin(plugin)
401             relevants_files[plugin] = module.createProject(self)
402         self.infos["RELEVANT_FILES"] = relevants_files
403
404     def _writeVersionFile(self, filename):
405         version_file = open(os.path.join(const.DATA_DIR, "vtemplates/VERSION"), "r").read()
406         open(filename, "w").write(versionFileGenerator(self, version_file))
407
408     def _writeProjectFile(self, filename):
409         f = open(filename, "w")
410         f.write(projectFileGenerator(self))
411         f.close()
412
413     def _writeMakefile(self):
414         bertos_utils.makefileGenerator(self)
415
416     def _writeUserMkFile(self):
417         bertos_utils.userMkGenerator(self)
418
419     def _writeUserMkFileFromPreset(self):
420         bertos_utils.userMkGeneratorFromPreset(self)
421
422     def _writeWizardMkFile(self):
423         bertos_utils.mkGenerator(self)
424
425     def _writeMainFile(self, filename):
426         main = open(os.path.join(const.DATA_DIR, "srctemplates/main.c"), "r").read()
427         open(filename, "w").write(main)
428
429     def _writeHwFiles(self, source_dir, destination_dir):
430         for module, information in self.infos["MODULES"].items():
431             for hwfile in information["hw"]:
432                 string = open(source_dir + "/" + hwfile, "r").read()
433                 hwfile_path = destination_dir + "/" + os.path.basename(hwfile)
434                 if not self.edit or not os.path.exists(hwfile_path):
435                     # If not in editing mode it copies all the hw files. If in
436                     # editing mode it copies only the files that don't exist yet
437                     open(os.path.join(destination_dir,os.path.basename(hwfile)), "w").write(string)
438
439     def _writeCfgFiles(self, source_dir, destination_dir):
440         for configuration, information in self.infos["CONFIGURATIONS"].items():
441             string = open(source_dir + "/" + configuration, "r").read()
442             for start, parameter in information["paramlist"]:
443                 infos = information[parameter]
444                 value = infos["value"]
445                 if "unsigned" in infos["informations"] and infos["informations"]["unsigned"]:
446                     value += "U"
447                 if "long" in infos["informations"] and infos["informations"]["long"]:
448                     value += "L"
449                 string = sub(string, parameter, value)
450             f = open(os.path.join(destination_dir, os.path.basename(configuration)), "w")
451             f.write(string)
452             f.close()
453
454     def _writeCustomSrcFiles(self):
455         origin = self.infos["PRESET_SRC_PATH"]
456         # Files to be ignored (all project files, cfg dir, wizard mk file, all global ignored dirs)
457         project_related_stuff = (
458             "cfg",
459             "hw",
460             self.infos["PRESET_NAME"] + ".mk",
461             self.infos["PRESET_NAME"] + "_user.mk",
462             "project.bertos",
463             self.infos["PRESET_NAME"] + ".project",
464             self.infos["PRESET_NAME"] + ".workspace",
465         ) + const.IGNORE_LIST
466         for element in os.listdir(origin):
467             if element not in project_related_stuff:
468                 full_path = os.path.join(origin, element)
469                 if os.path.isdir(full_path):
470                     copytree.copytree(full_path, os.path.join(self.prjdir, element), ignore_list=const.IGNORE_LIST)
471                 else:
472                     shutil.copy(full_path, self.prjdir)
473
474     def _setupAutoenabledParameters(self):
475         for module, information in self.infos["MODULES"].items():
476             if "configuration" in information and information["configuration"] != "":
477                 configurations = self.infos["CONFIGURATIONS"]
478                 configuration = configurations[information["configuration"]]
479                 for start, parameter in configuration["paramlist"]:
480                     if "type" in configuration[parameter]["informations"] and configuration[parameter]["informations"]["type"] == "autoenabled":
481                         configuration[parameter]["value"] = "1" if information["enabled"] else "0"
482                 self.infos["CONFIGURATIONS"] = configurations
483
484     # Project related properties
485     @property
486     def maindir(self):
487         return self.infos.get("PROJECT_PATH", None)
488
489     @property
490     def srcdir(self):
491         if self.maindir:
492             return os.path.join(self.maindir, "bertos")
493         else:
494             return None
495
496     @property
497     def prjdir(self):
498         return self.infos.get("PROJECT_SRC_PATH", None)
499
500     @property
501     def hwdir(self):
502         if self.prjdir:
503             return os.path.join(self.prjdir, "hw")
504         else:
505             return None
506
507     @property
508     def cfgdir(self):
509         if self.prjdir:
510             return os.path.join(self.prjdir, "cfg")
511         else:
512             return None
513
514     @property
515     def old_srcdir(self):
516         return self.infos.get("OLD_BERTOS_PATH", None)
517
518     # BeRTOS sources related properties
519     @property
520     def bertos_maindir(self):
521         return self.infos.get("BERTOS_PATH", None)
522
523     @property
524     def bertos_srcdir(self):
525         if self.bertos_maindir:
526             return os.path.join(self.bertos_maindir, "bertos")
527         else:
528             return None
529
530     @property
531     def src_hwdir(self):
532         if self.from_preset:
533             return os.path.join(self.infos["PRESET_PATH"], self.infos["PRESET_HW_PATH"])
534         else:
535             return self.bertos_maindir
536
537     @property
538     def from_preset(self):
539         return self.infos.get("PROJECT_FROM_PRESET", False)
540
541     @property
542     def is_preset(self):
543         return self.infos.get("PRESET", False)
544
545     def _createDirectory(self, directory):
546         if not directory:
547             return
548         if os.path.isdir(directory):
549             shutil.rmtree(directory, True)
550         os.makedirs(directory)
551
552     def _copySources(self, origin, destination):
553         # If not in editing mode it copies all the bertos sources in the /bertos subdirectory of the project
554         shutil.rmtree(destination, True)
555         copytree.copytree(origin + "/bertos", destination, ignore_list=const.IGNORE_LIST)
556
557     def _mergeSources(self, origin, destination, old_sources_dir):
558         if old_sources_dir:
559             mergeSources(destination, origin, old_sources_dir)
560
561     def setInfo(self, key, value):
562         """
563         Store the given value with the name key.
564         """
565         self.infos[key] = value
566
567     def info(self, key, default=None):
568         """
569         Retrieve the value associated with the name key.
570         """
571         if key in self.infos:
572             return copy.deepcopy(self.infos[key])
573         return default
574
575     def getCpuInfos(self):
576         cpuInfos = []
577         for definition in self.findDefinitions(const.CPU_DEFINITION):
578             cpuInfos.append(getInfos(definition))
579         return cpuInfos
580
581     def searchFiles(self, filename):
582         file_dict = self.infos["FILE_DICT"]
583         return [(filename, dirname) for dirname in file_dict.get(filename, [])]
584
585     def findDefinitions(self, ftype):
586         # Maintain a cache for every scanned BERTOS_PATH
587         definitions_dict = self._cached_queries.get(self.infos["BERTOS_PATH"], {})
588         definitions = definitions_dict.get(ftype, None)
589         if definitions is not None:
590             return definitions
591         file_dict = self.infos["FILE_DICT"]
592         definitions = []
593         for filename in file_dict:
594             if fnmatch.fnmatch(filename, ftype):
595                 definitions += [(filename, dirname) for dirname in file_dict.get(filename, [])]
596
597         # If no cache for the current BERTOS_PATH create an empty one
598         if not definitions_dict:
599             self._cached_queries[self.infos["BERTOS_PATH"]] = {}
600         # Fill the empty cache with the result
601         self._cached_queries[self.infos["BERTOS_PATH"]][ftype] = definitions
602         return definitions
603
604     def setEnabledModules(self, enabled_modules):
605         modules = self.infos["MODULES"]
606         files = {}
607         for module, information in modules.items():
608             information["enabled"] = module in enabled_modules
609             if information["enabled"]:
610                 for dependency in information["depends"]:
611                     if not dependency in modules:
612                         files[dependency] = files.get(dependency, 0) + 1
613         self.infos["MODULES"] = modules
614         self.infos["FILES"] = files
615
616     def __repr__(self):
617         return "<BProject:instance %d>%s" %(id(self), repr(self.infos))