1###############################################################################
2# Copyright (c) 2013 INRIA
3#
4# This program is free software; you can redistribute it and/or modify
5# it under the terms of the GNU General Public License version 2 as
6# published by the Free Software Foundation;
7#
8# This program is distributed in the hope that it will be useful,
9# but WITHOUT ANY WARRANTY; without even the implied warranty of
10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11# GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License
14# along with this program; if not, write to the Free Software
15# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16#
17# Authors: Daniel Camara  <daniel.camara@inria.fr>
18#          Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
19###############################################################################
20'''
21 Utils.py
22
23 This file stores the utility functions that are used by the different Bake
24 modules.
25'''
26
27import subprocess
28import os
29import shutil
30import sys
31from xml.etree import ElementTree
32from xml.dom import minidom
33from bake.Exceptions import TaskError
34
35def print_backtrace():
36    """ Prints the full trace of the exception."""
37
38    import sys
39    import traceback
40    trace = ""
41    exception = ""
42
43    exceptionHandling = True
44    if(not sys.exc_info()[0] or not sys.exc_info()[1]):
45        exceptionHandling = False
46
47    if exceptionHandling:
48        exc_list = traceback.format_exception_only (sys.exc_info()[0],sys.exc_info()[1])
49
50        for entry in exc_list:
51            exception += entry
52
53        tb_list = traceback.format_tb(sys.exc_info()[2])
54    else:
55        tb_list = traceback.format_stack()
56
57    for entry in tb_list:
58        trace += entry
59
60    toWrite = "\n%s\n%s" % (exception, trace)
61    sys.stderr.write(toWrite)
62    return toWrite
63
64def split_args(stringP):
65    """ Split arguments respecting aggregate strings."""
66
67    returnValue = []
68    rawSplit = stringP.split()
69    compensateElement=False
70    elementStr = ''
71    for element in rawSplit:
72        if "'" in element :
73            if element.count("'") % 2 != 0 :
74                if compensateElement :
75                    compensateElement = False
76                    returnValue.append(elementStr + " " + str(element))
77                    elementStr = ''
78                    element = None
79                elif element.find("'") == element.rfind("'") :
80                    compensateElement = True
81
82        if compensateElement :
83            if len(elementStr) > 0 :
84                elementStr = elementStr + " " + element
85            else :
86                elementStr = element
87        else :
88            if element :
89                returnValue.append(element)
90
91    return returnValue
92
93def prettify(elem):
94    """ Returns a pretty-printed XML string for the Element.
95    """
96    rough_string = ElementTree.tostring(elem, 'utf-8')
97    reparsed = minidom.parseString(rough_string)
98    string = reparsed.toprettyxml(indent="  ")
99    new_string=''
100    for line in string.split('\n'):
101        if line.strip():
102                new_string += line + '\n'
103
104    return new_string
105
106def mergeDirs(sourcePath, destPath):
107    """ Merge two folders, creating the structure and copying the files from
108        source to destination, when these are missing, and skipping files when
109        these are already present on the destination. Pay attention that what
110        this function does is a merge of directories contents not of file
111        content.
112    """
113
114    for root, dirs, files in os.walk(sourcePath):
115
116        #figure out where we're going
117        dest = destPath + root.replace(sourcePath, '')
118
119        #if we're in a directory that doesn't exist in the destination folder
120        #then create a new folder
121        if not os.path.isdir(dest):
122            os.mkdir(dest)
123#            print 'Directory created at: ' + dest
124
125        #loop through all files in the directory
126        for f in files:
127            #compute current (old) & new file locations
128            oldLoc = root + '/' + f
129            newLoc = dest + '/' + f
130
131            if not os.path.isfile(newLoc):
132                try:
133                    shutil.copy2(oldLoc, newLoc)
134#                    print 'File ' + f + ' copied.'
135                except IOError:
136#                    print 'file "' + f + '" already exists'
137                    pass
138
139
140class ModuleAttribute:
141    """ Definition of the Bake attribute. An attribute is basically one of the
142    options the user can have to configure the Bake usage.
143    """
144
145    def __init__(self, name, value, help, mandatory):
146        """ Initialization, all the fields are mandatory."""
147
148        self._name = name
149        self.value = value
150        self._help = help
151        self._mandatory = mandatory
152
153    @property
154    def name(self):
155        """ Returns the stored name of the attribute."""
156
157        return self._name
158
159    @property
160    def help(self):
161        """ Returns the help string attached to the attribute."""
162        return self._help
163
164    @property
165    def is_mandatory(self):
166        """ Returns if the attribute is mandatory or not."""
167        return self._mandatory
168
169
170class ModuleAttributeBase(object):
171    """ Definition of the Bake attribute structure. An attribute may be
172    organized in blocks, this structure stores this grouping of attributes.
173    """
174
175    def __init__(self):
176        self._attributes = dict()
177        self._children = []
178
179    def children(self):
180        """ Returns the children attributes attached to this attribute."""
181
182        return self._children
183
184    def add_child(self, child, name):
185        """ Attach a child attribute to this attribute."""
186
187        self._children.append([child, name])
188
189    def add_attribute(self, name, value, help, mandatory = False):
190        """ Creates a new attribute attached to this one."""
191
192        assert not name in self._attributes
193        self._attributes[name] = ModuleAttribute(name, value, help, mandatory)
194
195    def attributes(self):
196        """ Returns the list of attributes attached to this attribute block."""
197
198        return self._attributes.values()
199
200    def attribute(self, name):
201        """ Returns a specific attribute."""
202
203        if not name in self._attributes:
204            return None
205        else:
206            return self._attributes[name]
207
208class ColorTool:
209    """ Class responsible to handle the colored message printing."""
210
211    OK = '\033[34m'
212    WARNING = '\033[33m'
213    FAIL = '\033[91m'
214    ENDC = '\033[0m'
215
216    @classmethod
217    def has_colours(self, stream):
218        if not hasattr(stream, "isatty"):
219            return False
220        if not stream.isatty():
221            return False # auto color only on TTYs
222        try:
223            import curses
224            curses.setupterm()
225            return curses.tigetnum("colors") > 2
226        except:
227            # guess false in case of error
228            return False
229
230    @classmethod
231    def disable(self):
232        """ Disables the color print. """
233
234        ColorTool.OK = ''
235        ColorTool.WARNING = ''
236        ColorTool.FAIL = ''
237        ColorTool.ENDC = ''
238
239    @classmethod
240    def cPrint(self, color, message):
241        """ Print the message with the defined color. """
242
243        sys.stdout.write(color + message + self.ENDC)
244        sys.stdout.flush()
245
246    @classmethod
247    def cPrintln(self,color, message):
248        """ Print the message with the defined color and ends with a new line. """
249
250        self.cPrint(color, message + os.linesep)
251
252
253
254