1###############################################################################
2# Copyright (c) 2013 INRIA
3# Copyright (c) 2019 Mishal Shah (added search, getconf, install options)
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License version 2 as
7# published by the Free Software Foundation;
8#
9# This program is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12# GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License
15# along with this program; if not, write to the Free Software
16# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17#
18# Authors: Daniel Camara  <daniel.camara@inria.fr>
19#          Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
20#          Mishal Shah <shahmishal1998@gmail.com>
21###############################################################################
22'''
23 Bake.py
24
25 This is the main Bake file, it stores all the classes related to the
26 basic Bake operation. The class Bake is responsible to identify and
27 execute the defined options
28'''
29
30import xml.etree.ElementTree as ET
31try:
32 from xml.etree.ElementTree import ParseError
33except ImportError:
34 from xml.parsers.expat import ExpatError as ParseError
35import sys
36import os
37import distro
38import signal
39import copy
40import requests
41import bake.Utils
42from bake.Configuration import Configuration
43from bake.ModuleEnvironment import ModuleEnvironment
44from bake.ModuleLogger import StdoutModuleLogger, LogfileModuleLogger, LogdirModuleLogger
45from optparse import OptionParser
46from bake.Dependencies import Dependencies, DependencyUnmet
47from bake.Exceptions import MetadataError
48from bake.Utils import ColorTool
49from bake.Exceptions import TaskError
50from bake.ModuleSource import SystemDependency
51from bake.ModuleBuild import NoneModuleBuild
52from bake.Module import ModuleDependency
53from bake.ModuleAppStore  import BaseClient
54from bake.Constants import *
55
56
57def signal_handler(signal, frame):
58    """ Handles Ctrl+C keyboard interruptions """
59
60    print (os.linesep + ' > Bake was aborted! (Ctrl+C)')
61    os._exit(130)
62
63class MyOptionParser(OptionParser):
64    def format_description(self, formatter):
65        import os
66        import sys
67        return self.description % os.path.basename(sys.argv[0])
68
69
70class Bake:
71    """ Main Bake class """
72
73    main_options = ""
74
75    def __init__(self):
76        pass
77
78    def _error(self, string):
79        """ Handles hard exceptions, the kind of exceptions Bake should not
80        recover from."""
81
82        import sys
83        print(' > Error: %s ' % string)
84        if Bake.main_options.debug:
85            import bake.Utils
86            bake.Utils.print_backtrace()
87        else:
88            print('   For more information call Bake with --debug and/or'
89                  ' -v, -vvv, for full verbose mode (bake --help)')
90        sys.exit(1)
91
92    def _fix_config(self, config, args):
93        """Handles the fix_cinfig command line option. It intends to fix
94        manually changed files and updates the in-use configuration with
95        new values."""
96
97        parser = OptionParser(usage='usage: %prog fix-config [options]')
98        self._enable_disable_options(parser)
99        parser.add_option("-f", "--conffile", action="store", type="string",
100                          dest="bakeconf", default="bakeconf.xml",
101                          help="The Bake meta-data configuration from where to"
102                          " get the updated modules file to use. Default: %default.")
103        parser.add_option("--objdir", action="store", type="string",
104                          dest="objdir", default=None,
105                          help="The per-module directory where the object"
106                          " files of each module will be compiled.")
107        parser.add_option("--sourcedir", action="store", type="string",
108                          dest="sourcedir", default=None,
109                          help="The directory where the source code of all modules "
110                          "will be downloaded.")
111        parser.add_option("-i", "--installdir", action="store", type="string",
112                          dest="installdir", default=None,
113                          help="The directory where all modules will be installed.")
114
115        parser.add_option("-t", "--target-file", action="store", type="string",
116                          dest="targetfile", default=None,
117                          help="New target file, if not defined Bake"
118                          " overwrites the present configuration file.")
119
120        (options, args_left) = parser.parse_args(args)
121
122        if options.bakeconf == "bakeconf.xml":
123            options.bakeconf = self.check_configuration_file(options.bakeconf, False);
124
125        config = self.check_configuration_file(config, True)
126
127        contribconf = []
128        try:
129            for cfile in os.listdir("contrib"):
130                if cfile.endswith(".xml"):
131                    contribconf.append("contrib/"+cfile)
132        except Exception as e:
133            True
134
135        # Stores the present configuration
136        old_config = Configuration(config)
137        old_config.read()
138
139        if options.targetfile:
140            new_config = Configuration(options.targetfile,
141                                   relative_directory_root=old_config.get_relative_directory_root())
142        else:
143            new_config = Configuration(config,
144                                   relative_directory_root=old_config.get_relative_directory_root())
145
146
147        try:
148            new_config.read_metadata(options.bakeconf)
149        except Exception as e:
150            self._error('Problem reading Configuration file "%s" \n Error: %s'  % (options.bakeconf, str(e)))
151
152        for cconf in contribconf:
153            try:
154                new_config.read_metadata(cconf)
155            except Exception as e:
156                self._error('Problem reading Configuration file "%s" \n Error: %s'  % (cconf, str(e)))
157
158        # Checks if the directories where set and if so set the new config file
159        # with the new parameters, or let the old ones
160        if options.installdir:
161            new_config.set_installdir(options.installdir)
162        else:
163            new_config.set_installdir(old_config.get_installdir())
164        if options.objdir:
165            new_config.set_objdir(options.objdir)
166        else:
167            new_config.set_objdir(old_config.get_objdir())
168        if options.sourcedir:
169            new_config.set_sourcedir(options.sourcedir)
170        else:
171            new_config.set_sourcedir(old_config.get_sourcedir())
172
173        # copy installed files.
174        for old_module in old_config.modules():
175            new_module = new_config.lookup(old_module.name())
176            if new_module is None:
177                # ignore old modules that do not exist in the new configuration
178                continue
179            new_module.installed = old_module.installed
180
181        # copy which modules are enabled into new config
182        for old_module in old_config.enabled():
183            new_module = new_config.lookup(old_module.name())
184            if new_module is None:
185                # ignore old enabled modules that do not exist in the new configuration
186                continue
187            new_config.enable(new_module)
188
189        # copy which modules are disabled into new config
190        for old_module in old_config.disabled():
191            new_module = new_config.lookup(old_module.name())
192            if new_module is None:
193                # ignore old disabled modules that do not exist in the new configuration
194                continue
195            new_config.disable(new_module)
196
197        # now, parse new enabled/disabled options
198        self._parse_enable_disable(options, new_config)
199
200        # copy old variables into new config for all modules
201        for old_module in old_config.modules():
202            new_module = new_config.lookup(old_module.name())
203            if new_module is None:
204                # ignore old modules that do not exist in the new configuration
205                continue
206            old_build = old_module.get_build()
207            new_build = new_module.get_build()
208            for old_attribute in old_build.attributes():
209                if new_build.attribute(old_attribute.value) is None:
210                    continue
211                new_build.attribute(old_attribute.name).value = old_attribute.value
212
213        new_config.write()
214
215    def _enable_disable_options(self, parser):
216        """ Allows the parser to recognize --enable and --disable options."""
217
218        parser.add_option("-e", "--enable", action="append", type="string",
219                          dest="enable", default=[],
220                          help="A module to enable in the Bake configuration")
221        parser.add_option("-d", "--disable", action="append", type="string",
222                          dest="disable", default=[],
223                          help="A module to disable in the Bake configuration")
224        parser.add_option("-a", "--enable-all", action="store_true",
225                          dest="enable_all", default=None,
226                          help="Enable all modules.")
227        parser.add_option("-m", "--enable-minimal", action="store_true",
228                          dest="enable_minimal", default=None,
229                          help="Disable all non-mandatory dependencies.")
230
231    def resolve_contrib_dependencies (self, module, fmod, configuration):
232        """ Handles the contrib type dependencies"""
233        for dep in module.dependencies ():
234            dep_mod = configuration.lookup (dep._name)
235            if dep_mod.mtype() == "ns-contrib":
236                # Do not prepend contrib prefix to user supplied contrib name more than once
237                if not(module.get_source().attribute("module_directory").value.startswith(fmod + '/contrib')):
238                    dep_mod.get_source().attribute("module_directory").value = fmod+'/contrib/'+dep_mod.get_source().attribute("module_directory").value
239                dep_mod.addDependencies(ModuleDependency(fmod, False))
240                self.resolve_contrib_dependencies (dep_mod, fmod, configuration)
241
242    def _enable(self, enable, configuration):
243        """ Handles the --enable option, setting defined modules as enable."""
244        for module_name in enable:
245            module = configuration.lookup(module_name)
246            if not module:
247                self._error('Module "%s" not found' % module_name)
248            if module.mtype() == "ns-contrib":
249                found=0
250                fmod = None
251                for mod in enable:
252                    if configuration.lookup(mod).mtype() == "ns" and ((mod>=module.minver() and (module.maxver() is None or mod<=module.maxver())) or (mod == "ns-3-dev" and module.maxver() is None)):
253                        found+= 1
254                        fmod = mod
255                if not found==1:
256                    self._error('Module "%s" has unmet dependency: %s' % (module_name, module.minver()))
257
258                # Do not prepend contrib prefix to user supplied contrib name more than once
259                if not(module.get_source().attribute("module_directory").value.startswith(fmod + '/contrib')):
260                    module.get_source().attribute("module_directory").value = fmod+'/contrib/'+module.get_source().attribute("module_directory").value
261
262                module.addDependencies(ModuleDependency(fmod, False))
263                self.resolve_contrib_dependencies (module, fmod, configuration)
264            configuration.enable(module)
265
266    def _disable(self, disable, configuration):
267        """ Handles the --disable option, setting the defined modules as disable."""
268
269        for module_name in disable:
270            module = configuration.lookup(module_name)
271            if not module:
272                self._error('Module "%s" not found' % module_name)
273            configuration.disable(module)
274            if module.mtype() == "ns":
275                enabled_list = configuration.enabled()
276                for mod in enabled_list:
277                    if mod.mtype() == "ns-contrib":
278                        configuration.disable(mod)
279
280    def _variables_process(self, items, configuration, is_append):
281        """ Handles the defined configured variables ."""
282
283        for module_name, name, value in items:
284            if module_name:
285                module = configuration.lookup(module_name)
286                if not module:
287                    self._error('Module "%s" not found' % module_name)
288                if not module.get_build().attribute(name):
289                    self._error('Module "%s" has no attribute "%s"' %
290                                (module_name, name))
291                if is_append:
292                    module.get_build().attribute(name).value = \
293                        module.get_build().attribute(name).value + ' ' + value
294                else:
295                    module.get_build().attribute(name).value = value
296            else:
297                for module in configuration.modules():
298                    if module.get_build().attribute(name):
299                        if is_append and module.get_build().attribute(name).value :
300                            module.get_build().attribute(name).value = \
301                                module.get_build().attribute(name).value + ' ' + value
302                        else:
303                            module.get_build().attribute(name).value = value
304
305    def _parse_enable_disable(self, options, configuration):
306        """ Identify the enabled and disabled options passed as parameters
307        in the configuration.
308        """
309
310        # enables/disables the explicit enable/disable modules passed as argument
311        self._enable(options.enable, configuration)
312        for mod in options.disable:
313            if not mod in options.enable:
314                self._error('Module "%s" not enabled' % mod)
315        self._disable(options.disable, configuration)
316
317        # if the option -a is used, meaning all the modules should be enabled
318        if options.enable_all:
319            for module in configuration.modules():
320                configuration.enable(module)
321
322
323        # if the option -m is used, meaning the minimum configuration should be used
324        # it disables all the non mandatory dependencies
325        if options.enable_minimal:
326            enabled = []
327            def _enabled_iterator(module):
328                """ Assigns the module as enabled."""
329                enabled.append(module)
330                return True
331
332            self._iterate(configuration, _enabled_iterator,
333                          configuration.enabled(),
334                          follow_optional=True)
335            enabled_optional = []
336            def _enabled_optional_iterator(module):
337                enabled_optional.append(module)
338                return True
339            self._iterate(configuration, _enabled_optional_iterator,
340                          configuration.enabled(),
341                          follow_optional=False)
342            for module in enabled:
343                if not module in enabled_optional:
344                    configuration.disable(module)
345
346    def _parse_variable(self, string, configuration):
347        """ Verifies if the module and requested attribute exists."""
348
349        retval = []
350        data = string.split(":")
351
352        # if it is an setting for all the modules that contains such variable
353        if len(data) == 1:
354            name, value = string.split("=")
355            for module in configuration.modules():
356                if module.get_build().attribute(name):
357                    retval.append((module, name, value))
358            if not retval:
359                print ('Error: no module contains variable %s' % name)
360        # if it is a setting for a specific module
361        elif len(data) == 2:
362            name, value = data[1].split("=")
363            module = configuration.lookup(data[0])
364            if not module:
365                self._error('non-existing module %s in variable'
366                            ' specification %s' % (name, string))
367            if not module.get_build().attribute(name):
368                self._error('non-existing variable %s in module %s' %
369                            (name, module._name))
370            retval.append((module, name, value))
371        # if the variable is set incorrectly
372        else:
373            self._error('invalid variable specification: "%s"' % string)
374        return retval
375
376    def _read_resource_file(self, configuration):
377        """ Reads the predefined elements on the uer's resource file."""
378
379        rcPredefined = []
380        fileName = os.path.join(os.path.expanduser("~"), ".bakerc")
381
382        if os.path.isfile(fileName):
383            rcPredefined = configuration.read_predefined(fileName)
384
385        return rcPredefined
386
387    def _get_predefined(self, configuration):
388        """ Gets the values of enable and disable as a predefined setting."""
389
390        predefined =ET.Element('predefined', {'name':'last'})
391        for e in configuration._enabled:
392            enable_node = ET.Element('enable', {'name':e.name()})
393            predefined.append(enable_node)
394
395        for e in configuration._disabled:
396            enable_node = ET.Element('disable', {'name':e.name()})
397            predefined.append(enable_node)
398        return predefined
399
400
401    def save_resource_file(self, configuration, fileName):
402        """ Saves the pretty resource file."""
403
404        try:
405            fout = open(fileName, "w")
406            fout.write(bake.Utils.prettify(configuration))
407            fout.close()
408        except IOError as e:
409            ""
410            # print ('Problems writing the  resource file, error: %s' % e)
411
412    def _save_resource_configuration(self, configuration):
413        """ Saves the last call to the predefined elements on the
414        user's resource file.
415        """
416
417        allPredefined = []
418        fileName = os.path.join(os.path.expanduser("~"), ".bakerc")
419        lastConfig = self._get_predefined(configuration)
420
421        if os.path.isfile(fileName):
422            try:
423                et = ET.parse(fileName)
424                root = et.getroot()
425                for element in root.findall('predefined'):
426                    if element.attrib['name'] == "last":
427                        root.remove(element)
428                        break
429
430                root.append(lastConfig)
431                self.save_resource_file(root, fileName)
432                return
433            except ParseError as e :
434                print ('Problems reading the resource file, error: %s'% e)
435
436        # There is no configuration file, so wee need to create one
437        configuration = ET.Element('configuration', {})
438        configuration.append(lastConfig)
439        self.save_resource_file(configuration, fileName)
440
441    def _list(self, config, args):
442        """ Handles the list option for %prog """
443
444        # sets the options the parser should recognize for the configuration
445        parser = OptionParser(usage='usage: %prog list [options]')
446        parser.add_option("-f", "--conffile", action="store", type="string",
447                          dest="bakeconf", default="bakeconf.xml",
448                          help="The Bake meta-data configuration file to use. "
449                          "Default: %default.")
450        parser.add_option("-c", "--contrib", action="store_true",
451                          dest="contrib", default="False",
452                          help="Show only contrib modules.")
453        (options, args_left) = parser.parse_args(args)
454        listconf = Configuration(config)
455        contrib_list = []
456        module_list = []
457
458        contribconf = []
459        try:
460            for cfile in os.listdir("contrib"):
461                if cfile.endswith(".xml"):
462                    contribconf.append("contrib/"+cfile)
463        except Exception as e:
464            True
465
466        try:
467            listconf.read_metadata(options.bakeconf)
468        except Exception as e:
469            self._error('Problem reading Configuration file "%s" \n Error: %s'  % (options.bakeconf, str(e)))
470
471        for cconf in contribconf:
472            try:
473                listconf.read_metadata(cconf)
474            except Exception as e:
475                self._error('Problem reading Configuration file "%s" \n Error: %s'  % (cconf, str(e)))
476
477        for mod in listconf.modules():
478            if mod.mtype() == "ns-contrib":
479                contrib_list.append(mod.name())
480            elif not options.contrib == True:
481                module_list.append(mod.name())
482
483        contrib_list.sort()
484        module_list.sort()
485        for m in module_list:
486            print("module: "+m)
487        for c in contrib_list:
488            print("contrib: "+c)
489
490    def _configure(self, config, args):
491        """ Handles the configuration option for %prog """
492
493        # sets the options the parser should recognize for the configuration
494        parser = OptionParser(usage='usage: %prog configure [options]')
495        self._enable_disable_options(parser)
496        parser.add_option("-f", "--conffile", action="store", type="string",
497                          dest="bakeconf", default="bakeconf.xml",
498                          help="The Bake meta-data configuration file to use. "
499                          "Default: %default.")
500        parser.add_option("-g", "--gui", action="store_true",
501                          dest="gui", default="False",
502                          help="Use a GUI to define the configuration.")
503        parser.add_option("-s", "--set", action="append", type="string",
504                          dest="set",
505                          default=[],
506                          help="Format: module:name=value. A variable to set"
507                          " in the Bake configuration for the matching module.")
508        parser.add_option("--append", action="append", type="string",
509                          dest="append", default=[],
510                          help="Format: module:name=value. A variable to"
511                          " append to in the Bake build "
512                          "configuration for the especified module.")
513        parser.add_option("--objdir", action="store", type="string",
514                          dest="objdir", default="objdir",
515                          help="The per-module directory where the object"
516                          " files of each module will be compiled.")
517        parser.add_option("--sourcedir", action="store", type="string",
518                          dest="sourcedir", default="source",
519                          help="The directory where the source code of all modules "
520                          "will be downloaded.")
521        parser.add_option("-i", "--installdir", action="store", type="string",
522                          dest="installdir", default="build",
523                          help="The directory where all modules will be installed.")
524        parser.add_option("-p", "--predefined", action="store", type="string",
525                          dest="predefined", default=None,
526                          help="A predefined configuration to apply")
527
528        parser.add_option('--logfile', help='File in which we want to store log output '
529                          'of requested operation', action="store", type="string", dest="logfile",
530                          default='')
531        parser.add_option('--logdir', help='Directory in which we want to store log output '
532                          'of requested operation. One file per module.', action="store",
533                          type="string", dest="logdir",
534                          default='')
535        parser.add_option('-v', '--verbose', action='count', dest='verbose',
536                          default=0, help='Increase the log verbosity level')
537        parser.add_option('-q', '--quiet', action='count', dest='quiet',
538                          default=0, help='Increase the log quietness level')
539        parser.add_option("-c", "--clean", action="store_true",
540                          dest="remove", default=False,
541                          help="Remove all enabled modules")
542
543        # sets the configuration values got from the line command
544        (options, args_left) = parser.parse_args(args)
545        if options.bakeconf == "bakeconf.xml":
546            options.bakeconf = self.check_configuration_file(options.bakeconf, False);
547
548        contribconf = []
549        try:
550            for cfile in os.listdir("contrib"):
551                if cfile.endswith(".xml"):
552                    contribconf.append("contrib/"+cfile)
553        except Exception as e:
554            True
555
556        configuration = Configuration(config)
557
558        if not options.remove:
559            try:
560                configuration.read()
561                for m in  configuration.enabled():
562                    if m.name() not in options.enable:
563                        options.enable.append(m.name())
564            except Exception as e:
565                True
566
567        try:
568            configuration.read_metadata(options.bakeconf)
569        except Exception as e:
570            self._error('Problem reading Configuration file "%s" \n Error: %s'  % (options.bakeconf, str(e)))
571
572        for cconf in contribconf:
573            try:
574                configuration.read_metadata(cconf)
575            except Exception as e:
576                self._error('Problem reading Configuration file "%s" \n Error: %s'  % (cconf, str(e)))
577
578        configuration.set_sourcedir(options.sourcedir)
579        configuration.set_objdir(options.objdir)
580        configuration.set_installdir(options.installdir)
581
582        # if used the predefined settings, reads the predefined configuration
583        if options.predefined:
584            data = options.predefined.split(':')
585            requested = None
586            predefined = configuration.read_predefined(options.bakeconf)
587
588            # if the user has a bake configuration
589            rcPredefined = self._read_resource_file(configuration)
590            predefined = rcPredefined + predefined
591
592
593            if len(data) == 1:
594                requested = data[0]
595            elif len(data) == 2:
596                predefined += configuration.read_predefined(data[0])
597                requested = data[1]
598            else:
599                self._error('Invalid --predefined content: "%s"' % predefined)
600            for p in requested.split(','):
601                found = False
602                for predef in predefined:
603                    if predef.name == p:
604                        found = True
605                        self._enable(predef.enable, configuration)
606                        self._disable(predef.disable, configuration)
607                        self._variables_process(predef.variables_set,
608                                                configuration, is_append=False)
609                        self._variables_process(predef.variables_append,
610                                                configuration, is_append=True)
611                        directories = predef.directories
612                        if 'sourcedir' in directories:
613                            configuration.set_sourcedir(directories['sourcedir'])
614                        if 'objdir' in directories:
615                            configuration.set_objdir(directories['objdir'])
616                        if 'installdir' in directories:
617                            configuration.set_installdir(directories['installdir'])
618                        break
619                if not found:
620                    self._error('--predefined: "%s" not found.' % p)
621
622        # Registers the modules are that enabled/disabled
623        # handles the -a, -m, --disable, --enable tags
624        self._parse_enable_disable(options, configuration)
625
626        # handles the set command line option, to overwrite the specific
627        # module setting with the new specified value
628        for variable in options.set:
629            matches = self._parse_variable(variable, configuration)
630            for module, name, value in matches:
631                module.get_build().attribute(name).value = value
632
633        # handles the append command line option, to add the new
634        # value to the module setting
635        for variable in options.append:
636            matches = self._parse_variable(variable, configuration)
637            for module, name, value in matches:
638                current_value = module.get_build().attribute(name).value
639                module.get_build().attribute(name).value = current_value + ' ' + value
640        configuration.write()
641
642        if not configuration._enabled and not options.append and not options.remove:
643            env =  self._get_dummy_env(options)
644            env._logger.commands.write(' > No module enabled: Bake configuration requires at least one module to be enabled'
645                                       ' (enable, predefined), or appended.\n'
646                                       '   Argument(s) %s is not enough for an unambiguous action.\n' % (args_left))
647            self._error('No module enabled, please use -e <name of the module>, -p <predefined modules> or -a, to activate all modules.')
648
649        self._save_resource_configuration(configuration)
650
651
652    dependencyChain=None
653    def _iterate(self, configuration, functor, targets, follow_optional=True):
654        """Iterates over the configuration modules applying the functor
655        function and solve reminding dependencies.
656        """
657
658        deps = Dependencies()
659
660        # execute just one time to get the optional dependencies chain
661        if not self.dependencyChain:
662            deps.checkDependencies(targets,configuration.modules())
663            self.dependencyChain = deps.dependencies
664        else :
665            deps.dependencies= self.dependencyChain
666#
667#
668
669        class Wrapper:
670            def __init__(self, module):
671                self._module = module
672            def function(self):
673                retval = functor(self._module)
674                configuration.write()
675                return retval
676        # for all the modules saves the configuration
677        for m in configuration.modules():
678            wrapper = Wrapper(m)
679            deps.add_dst(m, wrapper.function)
680        # Review the dependencies of all the configured modules
681        for m in configuration.modules():
682            for dependency in m.dependencies():
683                src = configuration.lookup (dependency._name)
684
685                # verifies if the dependency really exists in the configuration
686                # if not we could have a problem of a corrupt, or badly
687                # configured xml file, e.g. misspelled module name
688                if src is None:
689                    self._error('Dependency "%s" not found' % dependency._name)
690
691                if not src in configuration.disabled():
692                    # if it is set to add even the optional modules, or the
693                    # dependency is not optional, add the module it depends on
694                    # as a dependency
695                    if follow_optional or not dependency.is_optional():
696                        deps.add_dep(src, m, optional=dependency.is_optional())
697
698        try:
699            deps.resolve(targets)
700#            deps.dump2(sys.stdout)
701        except DependencyUnmet as error:
702            if not error.method() =='':
703                errorAppend = ' ' + error.method()
704            else:
705                 errorAppend = ' failed'
706
707            self._error(' Critical dependency, module "' + error.failed().name()+'"' + errorAppend)
708
709    def _read_config(self, config, directory=None):
710        """Reads the configuration file."""
711
712        configuration = Configuration(config, directory)
713        if not configuration.read():
714            sys.stderr.write('The configuration file has been changed or has moved.\n'
715                             'Running \'fix-config\'. You should consider running it\n'
716                             'yourself to tweak some parameters if needed.\n')
717            self._fix_config(config, [])
718            print(">> " + config)
719            configuration = Configuration(config)
720            if not configuration.read():
721                self._error('Oops. \'fix-config\' did not succeed. You should consider\n'
722                            'deleting your bakefile and running \'configure\' again.')
723
724        return configuration
725
726    def _option_parser(self, operation_name):
727        """Adds generic options to the options parser. Receives the name of the
728        present option as parameter.
729        """
730
731        parser = OptionParser(usage='usage: %prog ' + operation_name + ' [options]')
732        parser.add_option('--logfile', help='File in which we want to store log output '
733                          'of requested operation', action="store", type="string", dest="logfile",
734                          default='')
735        parser.add_option('--logdir', help='Directory in which we want to store log output '
736                          'of requested operation. One file per module.', action="store",
737                          type="string", dest="logdir",
738                          default='')
739        parser.add_option('-v', '--verbose', action='count', dest='verbose',
740                          default=0, help='Increase the log verbosity level')
741        parser.add_option('-q', '--quiet', action='count', dest='quiet',
742                          default=0, help='Increase the log quietness level')
743        parser.add_option("-o", "--one", action="store", type="string",
744                          dest="one", default="",
745                          help="Process only the module specified.")
746        parser.add_option("-a", "--all", action="store_true",
747                          dest="all", default=False,
748                          help="Process all modules")
749        parser.add_option("--stop-on-error", action="store_true",
750                          dest="stopOnError", default=False,
751                          help="Stop on the first error found and do not advance while the error is not corrected.")
752        parser.add_option("-s", "--start", action="store", type="string",
753                          dest="start", default="",
754                          help="Process all modules enabled starting from the module specified.")
755        parser.add_option("--after", action="store", type="string",
756                          dest="after", default="",
757                          help="Process all modules enabled starting after the module specified.")
758        parser.add_option("-i", "--environment-file-identification",
759                          action="store", type="string",
760                          dest="environment_file_identification",
761                          default="bakeSetEnv.sh",
762                          help="Name of the environment setting file")
763        parser.add_option("-x", "--no-environment-file", action='store_true',
764                          dest='no_environment_file', default=False,
765                          help='Do not create the environment file for this run')
766        parser.add_option("--sudo", action="store_true",
767                          dest="call_with_sudo", default=False,
768                          help='Best effort attempt to install dependencies and modules, when'
769                          ' required,  using sudo. The user has to have sudo rights (be careful using it).')
770
771        return parser
772
773
774    def createEnvironment(self, config, options, directory=None):
775        """ Auxiliary function to create an instance of the module environment"""
776
777        configuration = self._read_config(config, directory)
778        if options.logdir == '' and options.logfile == '':
779            logger = StdoutModuleLogger()
780        elif options.logdir != '':
781            assert options.logfile == ''
782            logger = LogdirModuleLogger(options.logdir)
783        else:
784            assert options.logfile != ''
785            logger = LogfileModuleLogger(options.logfile)
786        verbose = options.verbose - options.quiet
787        verbose = verbose if verbose >= 0 else 0
788        logger.set_verbose(verbose)
789        env = ModuleEnvironment(logger,
790            configuration.compute_installdir(),
791            configuration.compute_sourcedir(),
792            configuration.get_objdir(),
793            Bake.main_options.debug)
794        return configuration, env
795
796    def _do_operation(self, config, options, functor, directory=None):
797        """Applies the function, passed as parameter, over the options."""
798
799        configuration, env = self.createEnvironment(config, options, directory)
800        must_disable = []
801        if options.one != '':
802            if options.all or options.start != '' or options.after != '':
803                self._error('incompatible options')
804            module = configuration.lookup(options.one)
805            functor(configuration, module, env)
806            configuration.write()
807        elif options.all:
808            if options.start != '' or options.after != '':
809                self._error('incompatible options')
810            def _iterator(module):
811                return functor (configuration, module, env)
812            self._iterate(configuration, _iterator, configuration.modules())
813        elif options.start != '':
814            if options.after != '':
815                self._error('incompatible options')
816            must_process = []
817            first_module = configuration.lookup(options.start)
818            def _iterator(module):
819                if module == first_module:
820                    must_process.append(0)
821                if len(must_process) != 0:
822                    return functor (configuration, module, env)
823                else:
824                    return True
825            self._iterate(configuration, _iterator, configuration.enabled())
826        elif options.after != '':
827            # this is a list because the inner function below
828            # is not allowed to modify the outer function reference
829            must_process = []
830            first_module = configuration.lookup(options.after)
831            def _iterator(module):
832                if len(must_process) != 0:
833                    return functor (configuration, module, env)
834                elif module == first_module:
835                    must_process.append(1)
836                return True
837            self._iterate(configuration, _iterator, configuration.enabled())
838        else:
839            def _iterator(module):
840                return functor (configuration, module, env)
841            self._iterate(configuration, _iterator, configuration.enabled())
842        return env
843
844    def _get_enabled_ns(self, config):
845        """ returns the enabled ns versions"""
846        config = self.check_configuration_file(config, True);
847
848        import os
849        if os.path.isfile(config):
850            configuration = self._read_config(config)
851        else:
852            print(" > Couldn't find the " + config + " configuration file. \n"
853                  "   Call bake with -f [full path configuration file name].\n")
854            return
855
856        enabled = []
857        def _iterator(module):
858            if module.mtype()=="ns":
859                enabled.append(module.name())
860            return True
861
862        self._iterate(configuration, _iterator, configuration.enabled())
863
864        return enabled
865
866    def _search(self, config, args):
867        """Handles the search command line option"""
868        parser = self._option_parser('search')
869
870        (options, args_left) = parser.parse_args(args)
871
872        ns_enabled = self._get_enabled_ns(config)
873
874        logger = StdoutModuleLogger()
875        webclient = BaseClient(logger, SEARCH_API)
876        # lists all the apps from the AppStore
877        if len(args_left) == 0:
878            response = webclient.search_api()
879            for app in response:
880                sys.stdout.write(app['name'] + " (" + app['app_type'] + ") - " + app['abstract'] + "\n")
881                sys.stdout.flush()
882        # lists the apps matching the substring from the AppStore
883        elif len(args_left) == 1:
884            response = webclient.search_api(args_left[0], ns_enabled)
885            for app in response:
886                sys.stdout.write(app['app']['name'] + " (" + app['version'] + ") - " + app['app']['abstract'] + "\n")
887                sys.stdout.flush()
888        else:
889            self._error("Please provide only one parameter to search for an app")
890
891
892    def _deploy(self, config, args):
893        """Handles the deploy command line option."""
894
895        print("Downloading, building and installing the selected modules and dependencies.")
896        print("Please, be patient, this may take a while!")
897        returnValue = self._download(config, args);
898        if not returnValue:
899            return self._build(config, args)
900
901
902    def _getconf(self, config, args):
903        """Handles the getconf command line option"""
904        parser = self._option_parser('getconf')
905        (options, args_left) = parser.parse_args(args)
906
907        ns_enabled = self._get_enabled_ns(config)
908
909        logger = StdoutModuleLogger()
910        webclient = BaseClient(logger, INSTALL_API, "http://localhost:8000")
911
912        if len(args_left) == 1 and ns_enabled is not None:
913            argument_module = args_left[0]
914            if len(argument_module.split("=="))==2:
915                sys.stdout.write("Collecting " + argument_module + "\n")
916                module_name, version = argument_module.split("==")[0], argument_module.split("==")[1]
917                response, bakefile_obj = webclient.install_api(module_name, version, ns=ns_enabled)
918            elif len(argument_module.split("="))==2:
919                self._error('Invalid requirement: %s \n\
920                = is not a valid operator. Did you mean == ?'  % args_left[0])
921            elif len(argument_module.split("=="))==1:
922                sys.stdout.write("Collecting " + argument_module + "\n")
923                module_name = args_left[0]
924                response, bakefile_obj = webclient.install_api(module_name, ns=ns_enabled)
925
926            # Create contrib directory if it does not exist
927            try:
928                os.mkdir('contrib')
929            except:
930                pass
931            # download the bakeconf xml file in contrib directory
932            with open("contrib/" + response['name'] + "-" + response['version'] + ".xml", 'wb') as f:
933                f.write(bakefile_obj.content)
934
935            return response
936        else:
937            self._error("ns not configured")
938
939
940    def _install(self, config, args):
941        """Handles the install command line option"""
942        parser = self._option_parser('install')
943        (options, args_left) = parser.parse_args(args)
944        res = self._getconf(config, args)
945        # Take an input of modules to enable/disable/enable-minimal
946        arguments_for_configure = []
947        arguments_for_configure.append("-e")
948        arguments_for_configure.append("ns-" + res['ns'])
949        arguments_for_configure.append("-e")
950        arguments_for_configure.append(res['name'])
951
952        ## Ask if the non-mandatory dependecies are to be disabled
953        enable_minimal = raw_input("Disable all non-mandatory dependencies? (Y/n): ")
954        if enable_minimal.lower() == 'y':
955            arguments_for_configure.append("-m")
956
957        configureResult = self._configure(config, arguments_for_configure)
958        if not configureResult:
959            self._deploy(config, [])
960
961
962    def _download(self, config, args):
963        """Handles the download command line option."""
964
965        parser = self._option_parser('download')
966        parser.add_option("--force_download", action='store_true',
967                          dest='force_download', default=False,
968                          help='Force the download of all modules again')
969
970
971        (options, args_left) = parser.parse_args(args)
972        def _do_download(configuration, module, env):
973
974            if module._source.name() == 'none':
975                return True
976
977            dependencyExists = False
978            if isinstance(module._source, SystemDependency):
979
980                sys.stdout.write (" >> Searching for system dependency " + module.name() + " - ")
981                sys.stdout.flush()
982                # We support one of the following three attributes:
983                # file_test, executable_test, and dependency_test (deprecated)
984                if (module._source.attribute('file_test').value is not None):
985                    dependencyExists = module._source._check_file_expression (
986                      module._source.attribute('file_test').value)
987                elif (module._source.attribute('executable_test').value is not None):
988                    dependencyExists = module._source._check_executable_expression (
989                      module._source.attribute('executable_test').value)
990                # XXX Deprecated attribute; will be removed in future
991                elif (module._source.attribute('dependency_test').value is not None):
992                    dependencyExists = module._source._check_dependency_expression(env,
993                      module._source.attribute('dependency_test').value)
994                elif (module._source.attribute('import_test').value is not None):
995                    dependencyExists = module._source._check_import(
996                      module._source.attribute('import_test').value)
997
998                # if the dependency exists there is nothing else to do
999                if (dependencyExists) :
1000                    env.start_source(module.name(), ".")
1001                    module.printResult(env, "Search", module.OK)
1002                    env.end_source()
1003                    return True
1004
1005            if not dependencyExists:
1006                # Dependency did not exist
1007                targetDir=''
1008                if module._source.attribute('module_directory') and not module._source.attribute('module_directory').value.strip() =='':
1009                    targetDir=' (target directory:%s)'%module._source.attribute('module_directory').value
1010
1011                if not isinstance(module._source, SystemDependency):
1012                    sys.stdout.write (" >> Downloading " + module.name() + targetDir + " - ")
1013                sys.stdout.flush()
1014                if env._logger._verbose > 0:
1015                    print()
1016
1017                env._sudoEnabled=options.call_with_sudo
1018                ModuleEnvironment._stopOnError=options.stopOnError
1019                valueToReturn=module.check_source_version(env)
1020
1021
1022                if valueToReturn:
1023                    return module.download(env, options.force_download)
1024                else:
1025                    if isinstance(module._source, SystemDependency):
1026                        module.printResult(env, "Dependency ", module.FAIL)
1027                    else:
1028                        module.printResult(env, "Download", module.FAIL)
1029
1030                    if isinstance(module._source, SystemDependency):
1031                        env._logger.commands.write(' Module: \"%s\" is required by other modules but it is not available on your system.\n'
1032                                        '     Ask your system admin or review your library database to add \"%s\"\n'
1033                                        '     More information from the module: \"%s\"\n'% (module.name(), module.name(),
1034                               module._source.attribute('more_information').value))
1035                        return False
1036
1037                    else:
1038                        tool = module._source.name()
1039                        raise TaskError('    Unavailable Downloading tool %s'
1040                                ' for module "%s". Try to call \"%s check\"\n' %
1041                                (tool, module.name(),
1042                                 os.path.basename(sys.argv[0])))
1043        self._do_operation(config, options, _do_download)
1044
1045    def _update(self, config, args):
1046        """Handles the update command line option."""
1047
1048        parser = self._option_parser('update')
1049        (options, args_left) = parser.parse_args(args)
1050        self._check_source_version(config, options)
1051
1052
1053        def _do_update(configuration, module, env):
1054            if module._source.name() == 'none':
1055                return True
1056
1057            targetDir=''
1058            if module._source.attribute('module_directory') and not module._source.attribute('module_directory').value.strip() =='':
1059                targetDir=' (target directory:%s)'%module._source.attribute('module_directory').value
1060
1061            if not isinstance(module._source, SystemDependency):
1062                sys.stdout.write (" >> Updating " + module.name() + targetDir + " - ")
1063            sys.stdout.flush()
1064            if env._logger._verbose > 0:
1065                print()
1066
1067            return module.update(env)
1068
1069        self._do_operation(config, options, _do_update)
1070
1071    def _check_build_version(self, config, options):
1072        """Checks if the required build tools are available in the machine."""
1073
1074        def _do_check(configuration, module, env):
1075            if not module.check_build_version(env):
1076                env._logger.commands.write('    Unavailable building tool for'
1077                                            ' module "%s"\n' % module.name())
1078                return False
1079            return True
1080        self._do_operation(config, options, _do_check)
1081
1082
1083    def _check_source_version(self, config, options):
1084        """Checks if the source can be handled by the programs in the machine."""
1085        okForTool=True
1086        def _do_check(configuration, module, env):
1087            if not module.check_source_version(env):
1088                env._logger.commands.write('    Unavailable source tool'
1089                                            ' for module %s\n' % module.name())
1090                okForTool=False
1091                return False
1092            return True
1093        self._do_operation(config, options, _do_check)
1094        return okForTool
1095
1096    def _check_source_code(self, config, options, directory=None):
1097        """ Checks if we have already downloaded the matching source code."""
1098
1099        def _do_check(configuration, module, env):
1100            if not module.is_downloaded(env):
1101                env._logger.commands.write('    Unavailable source code for'
1102                                            ' module %s. Try %s download first.\n'
1103                                             %(module.name(), sys.argv[0]))
1104                return False
1105            return True
1106        self._do_operation(config, options, _do_check, directory)
1107
1108
1109    def _build(self, config, args):
1110        """Handles the build command line option."""
1111
1112        parser = self._option_parser('build')
1113        parser.add_option('-j', '--jobs', help='Allow N jobs at once.'
1114                          ,type='int', action='store',
1115                          dest='jobs', default=-1)
1116        parser.add_option('--force-clean', help='Forces the call of the clean'
1117                          ' option for the build.', action="store_true",
1118                          default=False, dest='force_clean')
1119        (options, args_left) = parser.parse_args(args)
1120        #self._check_build_version(config, options)
1121        self._check_source_code(config, options)
1122
1123        def _do_build(configuration, module, env):
1124
1125            if isinstance(module._source, SystemDependency) or isinstance(module._build, NoneModuleBuild) :
1126                if isinstance(module._build, NoneModuleBuild):
1127                    # Only to threat the variables and pre and post instalation
1128                    # that may be set even for none build kind of modules
1129                    module.build(env, options.jobs, options.force_clean)
1130                return True
1131
1132            sys.stdout.write(" >> Building " + module.name()  + " - ")
1133
1134            sys.stdout.flush()
1135            if env._logger._verbose > 0:
1136                print
1137
1138            env._sudoEnabled=options.call_with_sudo
1139            ModuleEnvironment._stopOnError=options.stopOnError
1140
1141            if module.check_build_version(env):
1142                retval = module.build(env, options.jobs, options.force_clean)
1143                if retval:
1144                    module.update_libpath(env)
1145                return retval
1146            else:
1147                module.printResult(env, "Building", module.FAIL)
1148                print("   >> Unavailable building tool for module %s, install %s"
1149                      %(module.name(),module._build.name()))
1150
1151        env = self._do_operation(config, options, _do_build)
1152
1153        if not options.no_environment_file:
1154            env.create_environment_file(options.environment_file_identification)
1155
1156    def _clean(self, config, args):
1157        """Handles the clean command line option."""
1158
1159        parser = self._option_parser('clean')
1160        (options, args_left) = parser.parse_args(args)
1161        self._check_build_version(config, options)
1162
1163        def _do_clean(configuration, module, env):
1164            if isinstance(module._source, SystemDependency) or isinstance(module._build, NoneModuleBuild):
1165                return True
1166
1167            sys.stdout.write(" >> Clean " + module.name()  + " - ")
1168            module.clean(env)
1169            return True
1170        self._do_operation(config, options, _do_clean)
1171
1172    def _distclean(self, config, args):
1173        """Handles the distclean command line option."""
1174
1175        parser = self._option_parser('distclean')
1176        (options, args_left) = parser.parse_args(args)
1177
1178
1179        def _do_distclean(configuration, module, env):
1180            if isinstance(module._source, SystemDependency) or isinstance(module._build, NoneModuleBuild):
1181                return True
1182
1183            sys.stdout.write(" >> Distribution clean " + module.name()  + " - ")
1184            returnValue = module.distclean(env)
1185            return True
1186        self._do_operation(config, options, _do_distclean)
1187
1188    def _fullclean(self, config, args):
1189        """Handles the fullclean command line option."""
1190
1191        parser = self._option_parser('fullclean')
1192        (options, args_left) = parser.parse_args(args)
1193
1194        def _do_fullclean(configuration, module, env):
1195            if isinstance(module._source, SystemDependency) or isinstance(module._build, NoneModuleBuild):
1196                return True
1197
1198            returnValue = module.fullclean(env)
1199            return returnValue
1200        self._do_operation(config, options, _do_fullclean)
1201
1202    def _uninstall(self, config, args):
1203        """Handles the uninstall command line option."""
1204
1205        parser = self._option_parser('uninstall')
1206        (options, args_left) = parser.parse_args(args)
1207        def _do_uninstall(configuration, module, env):
1208            sys.stdout.write(" >> Uninstall " + module.name()  + " - ")
1209            module.uninstall(env)
1210            return True
1211        self._do_operation(config, options, _do_uninstall)
1212
1213    def _shell(self, config, args):
1214        """Handles the shell command line option."""
1215
1216        parser = self._option_parser('build')
1217        (options, args_left) = parser.parse_args(args)
1218
1219        def _do_env_update(configuration, module, env):
1220            module.update_libpath(env)
1221            return True
1222        env = self._do_operation(config, options, _do_env_update)
1223        import os
1224        env.run([os.environ['SHELL']], directory=env.objdir, interactive=True)
1225
1226    def _check(self, config, args):
1227        """Handles the check command line option."""
1228
1229        checkPrograms = [['python3', 'Python3'],
1230                         ['hg', 'Mercurial'],
1231                         # ['cvs', 'CVS'],
1232                         ['git', 'Git'],
1233                         # ['bzr', 'Bazaar'],
1234                         ['tar', 'Tar tool'],
1235                         ['unzip', 'Unzip tool'],
1236                         # ['unrar', 'Unrar tool'],
1237                         # ['7z', '7z  data compression utility'],
1238                         # ['unxz', 'XZ data compression utility'],
1239                         ['make', 'Make'],
1240                         ['cmake', 'cMake'],
1241                         ['patch', 'patch tool'],
1242                         # ['autoreconf', 'autoreconf tool']
1243                         ]
1244        if sys.platform == 'darwin':
1245            checkPrograms.insert(1,['clang++', 'Clang C++ compiler'])
1246        else:
1247            checkPrograms.insert(1,['g++', 'GNU C++ compiler'])
1248        parser = self._option_parser('build')
1249        (options, args_left) = parser.parse_args(args)
1250        def _do_env_check(configuration, module, env):
1251            return True
1252
1253        env = self._get_dummy_env(options)
1254        colorTool = ColorTool()
1255        for element in checkPrograms:
1256            if env.check_program(element[0]):
1257                colorTool.cPrintln(colorTool.OK, " > " + element[1] + " - OK")
1258            else:
1259                colorTool.cPrintln(colorTool.WARNING, " > " + element[1] +
1260                                 " - is missing")
1261        print ('\n')
1262        colorTool.cPrint(colorTool.OK, " > Path searched for tools:")
1263        for item in env.path_list():
1264            sys.stdout.write (' ' + item)
1265            sys.stdout.flush()
1266        print ('\n')
1267
1268    def _get_dummy_env(self, options):
1269        """ Returns a dummy environment just for verifying the user's system configuration. """
1270        configuration = Configuration("")
1271
1272        if options.logdir == '' and options.logfile == '':
1273            logger = StdoutModuleLogger()
1274        elif options.logdir != '':
1275            assert options.logfile == ''
1276            logger = LogdirModuleLogger(options.logdir)
1277        else:
1278            assert options.logfile != ''
1279            logger = LogfileModuleLogger(options.logfile)
1280        verbose = options.verbose - options.quiet
1281        verbose = verbose if verbose >= 0 else 0
1282        logger.set_verbose(verbose)
1283        logger._update_file(logger._file)
1284
1285        return ModuleEnvironment(logger, "","","", Bake.main_options.debug)
1286
1287    def _show_one_builtin(self, builtin, string, variables):
1288        """Go over the available builtins handling tools."""
1289
1290        import textwrap
1291        if builtin.name() != 'none':
1292            print ('%s %s' % (string, builtin.name()))
1293            if variables:
1294                for attribute in builtin().attributes():
1295                    print ('    %s=%s' % (attribute.name, attribute.value))
1296                    lines = ['      %s' % line for line in textwrap.wrap(attribute.help)]
1297                    print ('\n'.join(lines))
1298
1299    def _show_variables(self, module):
1300        """Handles the show the variables available for source and build."""
1301
1302        source = module.get_source()
1303        if source.attributes():
1304            print ('  source %s' % source.name())
1305            for attribute in source.attributes():
1306                print ('    %s=%s' % (attribute.name, attribute.value))
1307        build = module.get_build()
1308
1309        if build.attributes():
1310            print ('  build %s' % build.name())
1311            for attribute in build.attributes():
1312                print ('    %s=%s' % (attribute.name, attribute.value))
1313
1314    def _show_builtin(self, config, args):
1315        """Handles the show one builtin command line option."""
1316
1317        from bake.ModuleSource import ModuleSource
1318        from bake.ModuleBuild import ModuleBuild
1319        parser = OptionParser(usage='usage: %prog show [options]')
1320        parser.add_option('-a', '--all', action='store_true', dest='all',
1321                          default=False,
1322                          help='Display all known information about builtin source and build commands')
1323        parser.add_option('--source', action='store_true', dest='source',
1324                          default=False,
1325                          help='Display information about builtin source commands')
1326        parser.add_option('--build', action='store_true', dest='build',
1327                          default=False,
1328                          help='Display information about builtin build commands')
1329        parser.add_option('--variables', action='store_true', dest='variables',
1330                          default=False,
1331                          help='Display variables for builtin commands')
1332        (options, args_left) = parser.parse_args(args)
1333
1334        if options.all :
1335            options.source = True
1336            options.build = True
1337            options.variables = True
1338        elif not options.source and not options.build :
1339            options.source = True
1340            options.build = True
1341
1342
1343        if options.source:
1344            for source in ModuleSource.subclasses():
1345                self._show_one_builtin(source, 'source', options.variables)
1346
1347        if options.build:
1348            for build in ModuleBuild.subclasses():
1349                self._show_one_builtin(build, 'build', options.variables)
1350
1351    systemDependencies=dict()
1352
1353    def show_module(self, state, options, config, label):
1354        """ Handles the printing of the information of modules and dependencies."""
1355
1356        depen=dict()
1357
1358        if not state:
1359            return
1360        for mod in state:
1361            if mod.mtype():
1362                print('module %s: %s (%s)' % (mod.mtype(), mod.name(), label))
1363            else:
1364                print('module: %s (%s)' % (mod.name(), label))
1365            dependencies = mod.dependencies()
1366
1367            # Stores the system dependencies
1368            if isinstance(mod._source, SystemDependency) and label=="enabled":
1369                self.systemDependencies[mod.name()] = mod._source
1370
1371            # Collects the dependencies
1372            if not mod.name() in depen:
1373                depen[mod.name()] = dict()
1374
1375            if dependencies and not options.brief == True:
1376                print('  depends on:')
1377                for dependsOn in mod.dependencies():
1378                    print('     %s (optional:%s)' %
1379                          (dependsOn._name, dependsOn.is_optional()))
1380                    depen[mod.name()][dependsOn._name]=  dependsOn.is_optional()
1381            elif not options.brief == True:
1382                print('  No dependencies!')
1383
1384
1385            if options.variables:
1386                self._show_variables(mod)
1387
1388        if options.enabledTree and label=="enabled":
1389            print("\n-- Enabled modules dependency tree --")
1390            self.deptree(depen, depen, label, dict(), "", " ")
1391
1392        return mod
1393
1394    def showSystemDependencies(self, systemDependencies,config):
1395        """ Shows the System dependencies of the defined configuration. """
1396
1397        if len(systemDependencies)<=0:
1398            return
1399
1400        print ("\n-- System Dependencies --")
1401
1402        (distribution, version, version_id) = distro.linux_distribution()
1403
1404        if not distribution:
1405            distribution = 'darwin' # osName
1406        else:
1407            distribution = distribution.lower()
1408
1409        missing=False
1410        returnValue=""
1411        depend_keys = systemDependencies.keys()
1412        depend_keys=sorted(depend_keys)
1413
1414        # creates the environment
1415        configuration = self._read_config(config)
1416        logger = StdoutModuleLogger()
1417        logger.set_verbose(0)
1418        env = ModuleEnvironment(logger, "","",
1419            configuration.get_objdir())
1420
1421        for this_key in depend_keys:
1422            sysDep=systemDependencies[this_key]
1423            dependencyExists = False
1424
1425            # We support one of the following three attributes:
1426            # file_test, executable_test, and dependency_test (deprecated)
1427            if (sysDep.attribute('file_test').value is not None):
1428                dependencyExists = sysDep._check_file_expression (
1429                  sysDep.attribute('file_test').value)
1430            elif (sysDep.attribute('executable_test').value is not None):
1431                dependencyExists = sysDep._check_executable_expression (
1432                  sysDep.attribute('executable_test').value)
1433            # XXX Deprecated attribute; will be removed in future
1434            elif (sysDep.attribute('dependency_test').value is not None):
1435                dependencyExists = sysDep._check_dependency_expression(env,
1436                  sysDep.attribute('dependency_test').value)
1437            elif (sysDep.attribute('import_test').value is not None):
1438                dependencyExists = sysDep._check_import(
1439                  sysDep.attribute('import_test').value)
1440
1441            if not dependencyExists:
1442                sys.stdout.write(" > " + this_key + " - ")
1443                ColorTool.cPrintln(ColorTool.FAIL, "Missing")
1444                print("   >> " + sysDep.attribute('more_information').value)
1445                command = sysDep._get_command(distribution)
1446                command = command.strip()
1447                if not command == '':
1448                    installerName = sysDep.attribute('name_' + command.split()[0]).value
1449
1450                    # if didn't find the specific installer name uses the default one
1451                    if(not installerName):
1452                        installerName = this_key
1453
1454                    if (sysDep.attribute('import_test').value is None):
1455                        print('   >> Try: "sudo ' + command + ' ' +
1456                              installerName + '", if you have sudo rights.')
1457
1458                missing = True
1459            else:
1460                sys.stdout.write(" > " + this_key + " - ")
1461                ColorTool.cPrintln(ColorTool.OK, "OK")
1462
1463            returnValue= returnValue + this_key
1464
1465        # if there is a missing dependency the system error level is set to 1
1466        if missing:
1467            sys.exit(1)
1468
1469        return returnValue
1470
1471    def deptree(self, fulldep, depen, key, has_passed, optionalModule, padding):
1472        """ Shows the dependency tree. """
1473
1474        sys.stdout.write(padding[:-1] + '+-' + key + '/')
1475        color=ColorTool.FAIL
1476        if 'optional' in optionalModule:
1477            color=ColorTool.OK
1478        ColorTool.cPrintln(color, optionalModule)
1479        padding = padding + ' '
1480
1481        # to avoid loops
1482        if key in has_passed:
1483            sys.stdout.write(padding)
1484            ColorTool.cPrintln(ColorTool.FAIL, "> Cyclic Dependency")
1485            return "> Cyclic Dependency."
1486        else:
1487            has_passed[key]=True
1488
1489        depend_keys = depen.keys()
1490        depend_keys=sorted(depend_keys)
1491
1492        # goes recursively over the list of keys reading the dictionaries with
1493        # the dependencies
1494        count = 0
1495        listStr = ''
1496        for this_key in depend_keys:
1497            count += 1
1498            print (padding + '|')
1499            optional=""
1500            color=ColorTool.FAIL
1501            if this_key in depen and isinstance(depen[this_key],bool)>0:
1502                if depen[this_key]:
1503                    optional = " (optional)"
1504                    color=ColorTool.OK
1505                else:
1506                    optional = " (mandatory)"
1507
1508            if this_key in fulldep and len(fulldep[this_key])>0:
1509
1510                if count == len(depend_keys):
1511                    listStr = listStr + self.deptree(fulldep, fulldep[this_key], this_key, has_passed, optional, padding + ' ')
1512                else:
1513                    listStr = listStr + self.deptree(fulldep, fulldep[this_key], this_key, has_passed, optional, padding + '|')
1514            else:
1515                if this_key in fulldep:
1516                    sys.stdout.write(padding + '+-' + this_key)
1517                    ColorTool.cPrintln(color, optional)
1518                listStr = this_key +'.'+ listStr
1519
1520        del has_passed[key]
1521        return key +'/'+ listStr
1522
1523    def _print_version(self):
1524        print(" > Bake Version 0.1")
1525
1526    def _show(self, config, args):
1527        """Handles the show command line option."""
1528
1529        parser = OptionParser(usage='usage: %prog show [options]')
1530        parser.add_option('-a', '--all', action='store_true', dest='all',
1531                          default=False,
1532                          help='Display all known information about current configuration')
1533        parser.add_option('--enabled', action='store_true', dest='enabled',
1534                          default=False, help='Display information about existing enabled modules')
1535        parser.add_option('--disabled', action='store_true', dest='disabled',
1536                          default=False, help='Display information about existing disabled modules')
1537        parser.add_option('--available', action='store_true', dest='available',
1538                          default=False, help='Display information about available modules')
1539        parser.add_option('--variables', action='store_true', dest='variables',
1540                          default=False,
1541                          help='Display information on the variables set for the modules selected')
1542        parser.add_option('--predefined', action='store_true', dest='predefined',
1543                          default=False,
1544                          help='Display information on the items predefined')
1545        parser.add_option('--directories', action='store_true', dest='directories',
1546                          default=False,
1547                          help='Display information about which directories have been configured')
1548        parser.add_option('--enabledTree', action='store_true', dest='enabledTree',
1549                          default=False,
1550                          help='Shows the enabled modules dependency tree')
1551        parser.add_option('--showSystemDep', action='store_true', dest='showSystemDep',
1552                          default=True,
1553                          help='Shows the system dependency of the enabled/disabled modules')
1554        parser.add_option('-b', '--brief', action='store_true', dest='brief',
1555                          default=False,
1556                          help='Show only the module name')
1557        parser.add_option('-c', '--configured', action='store_true', dest='configured',
1558                          default=False,
1559                          help='Show only the configured module')
1560        (options, args_left) = parser.parse_args(args)
1561        # adds a default value so that show will show something even if there is
1562        # no option
1563        if not args:
1564            options.enabled = True
1565            options.showSystemDep = True
1566        else:
1567            if not options.disabled and not options.enabled and not options.configured:
1568                options.enabled=True
1569
1570        config= self.check_configuration_file(config, True);
1571
1572        import os
1573        if os.path.isfile(config):
1574            configuration = self._read_config(config)
1575        else:
1576            # try to get the default
1577            print(" > Couldn't find the " + config + " configuration file. \n"
1578                  "   Call bake with -f [full path configuration file name].\n")
1579            return
1580        if options.all:
1581            options.enabled = True
1582            options.disabled = True
1583            options.directories = True
1584            options.variables = True
1585            options.predefined = True
1586            options.enabledTree = True
1587        elif options.available:
1588            options.enabled = True
1589            options.disabled = True
1590
1591        if options.directories:
1592            print ('installdir   : ' + configuration.compute_installdir())
1593            print ('sourcedir    : ' + configuration.compute_sourcedir())
1594            print ('objdir       : ' + configuration.get_objdir())
1595
1596
1597        enabled = []
1598        def _iterator(module):
1599            enabled.append(module)
1600            return True
1601        self._iterate(configuration, _iterator, configuration.enabled())
1602        disabled = filter(lambda module: not module in enabled, configuration.modules())
1603
1604        if options.enabled:
1605            self.show_module(enabled, options, config, 'enabled')
1606
1607        if options.configured:
1608            self.show_module(configuration.configured(), options, config, 'configured')
1609
1610        if options.disabled:
1611            self.show_module(disabled, options, config, 'disabled')
1612
1613        if options.showSystemDep:
1614            self.showSystemDependencies(self.systemDependencies, config)
1615
1616
1617    def check_configuration_file(self, configFile, considersTemplate=False):
1618        """ Checks if the configuration file exists, if not tries to use the
1619        one on the root bake directory."""
1620
1621        # If the name is not the default one... do not interfere
1622        if configFile != "bakeconf.xml" and configFile != "bakefile.xml":
1623            return configFile
1624
1625        # If the file is the default, and exists on the local directory, fine
1626        if os.path.isfile(configFile):
1627            return configFile
1628
1629        presentDir = os.path.dirname(sys.argv[0])
1630        if not presentDir:
1631            presentDir = "."
1632        # if the file does not exist on the local directory
1633        # tries the standard configuration file on the installation directory
1634        if os.path.isfile(os.path.join(presentDir, configFile)):
1635            return os.path.join(presentDir, configFile)
1636
1637        # if the standard file does not exist
1638        # tries the generic configuration file on the installation directory
1639        if  considersTemplate and os.path.isfile(os.path.join(presentDir,
1640                                                              "bakeconf.xml")):
1641            return os.path.join(presentDir,"bakeconf.xml")
1642
1643        # if everything else fail.... returns the same name
1644        return configFile
1645
1646    options = ""
1647
1648    def checkPythonVersion(self):
1649        """ Checks the version  of the user's machine python. python-2.6 or
1650            2.7 supported"""
1651
1652        if sys.hexversion < 0x02060000:
1653            print(">>> Old Python version detected, please use python2 >= version 2.6")
1654            sys.exit(1)
1655
1656    def main(self, argv):
1657        """Main Bake function."""
1658
1659        # catches Ctrl-c
1660        signal.signal(signal.SIGINT, signal_handler)
1661
1662        self.checkPythonVersion()
1663
1664        parser = MyOptionParser(usage='usage: %prog [options] command [command options]',
1665                                description="""Where command is one of:
1666  deploy       : Downloads the configured modules AND makes the build in one step
1667  configure    : Setup the build configuration (source, build, install directory,
1668                 and per-module build options) from the module descriptions
1669  fix-config   : Update the build configuration from a newer module description
1670  search       : Search for modules from the AppStore
1671  getconf      : Downloads the bakefile for the module
1672  install      : Download and build the module
1673  download     : Download all modules enabled during configure
1674  update       : Update the source tree of all modules enabled during configure
1675  build        : Build all modules enabled during configure
1676  clean        : Cleanup the source tree of all modules built previously
1677  shell        : Start a shell and setup relevant environment variables
1678  uninstall    : Remove all files that were installed during build
1679  distclean    : Call the modules distclean option, if available
1680  fullclean    : Remove all the build AND source files
1681  show         : Report on build configuration
1682  show-builtin : Report on builtin source and build commands
1683  check        : Checks if all the required tools are available on the system
1684
1685To get more help about each command, try:
1686  %s command --help
1687""")
1688        parser.add_option("-f", "--file", action="store", type="string",
1689                          dest="config_file", default="bakefile.xml",
1690                          help="The Bake file to use, and the target "
1691                          "configuration/reconfiguration. Default: %default.")
1692        parser.add_option("--debug", action="store_true",
1693                          dest="debug", default=False,
1694                          help="Prints out all the error messages and problems.")
1695        parser.add_option("--noColor", action="store_true",
1696                          dest="noColor", default=False,
1697                          help='Print messages with no color')
1698        parser.add_option("-V", action="store_true",
1699                          dest="version", default=False,
1700                          help='Prints the version of Bake' )
1701        parser.disable_interspersed_args()
1702        (options, args_left) = parser.parse_args(argv[1:])
1703
1704        if options.version:
1705            self._print_version()
1706
1707
1708        Bake.main_options = options
1709
1710        # if asked to not having collors, useful for non iteractive
1711        # use and users that do not have a color enabled terminal
1712        if options.noColor:
1713            ColorTool.disable()
1714        else:
1715            has_colours = ColorTool.has_colours(sys.stdout)
1716            if not has_colours:
1717                ColorTool.disable()
1718
1719
1720        if len(args_left) == 0:
1721            if not options.version:
1722                parser.print_help()
1723            sys.exit(1)
1724        ops = [ ['deploy', self._deploy],
1725                ['configure', self._configure],
1726                ['fix-config', self._fix_config],
1727                ['search', self._search],
1728                ['getconf', self._getconf],
1729                ['install', self._install],
1730                ['download', self._download],
1731                ['update', self._update],
1732                ['build', self._build],
1733                ['clean', self._clean],
1734                ['shell', self._shell],
1735                ['uninstall', self._uninstall],
1736                ['distclean', self._distclean],
1737                ['fullclean', self._fullclean],
1738                ['show', self._show],
1739                ['show-builtin', self._show_builtin],
1740                ['check', self._check],
1741                ['list', self._list],
1742               ]
1743        recognizedCommand = False
1744
1745        for name, function in ops:
1746            if args_left[0].lower() == name:
1747                recognizedCommand = True
1748                if options.debug:
1749                    function(config=options.config_file, args=args_left[1:])
1750                else:
1751                    try:
1752                        function(config=options.config_file, args=args_left[1:])
1753                    except Exception as e:
1754                        print ('\n'+ str(e))
1755                        sys.exit(1)
1756                    except TaskError as e:
1757                        print ('\n'+e.reason)
1758                        sys.exit(1)
1759
1760        if not recognizedCommand:
1761            print (' >> Unrecognized option: ' + args_left[0])
1762            sys.exit(1)
1763
1764