utils.py 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. # ex:ts=4:sw=4:sts=4:et
  2. # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
  3. """
  4. BitBake Utility Functions
  5. """
  6. # Copyright (C) 2004 Michael Lauer
  7. #
  8. # This program is free software; you can redistribute it and/or modify
  9. # it under the terms of the GNU General Public License version 2 as
  10. # published by the Free Software Foundation.
  11. #
  12. # This program is distributed in the hope that it will be useful,
  13. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. # GNU General Public License for more details.
  16. #
  17. # You should have received a copy of the GNU General Public License along
  18. # with this program; if not, write to the Free Software Foundation, Inc.,
  19. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  20. digits = "0123456789"
  21. ascii_letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
  22. import re, fcntl, os
  23. def explode_version(s):
  24. r = []
  25. alpha_regexp = re.compile('^([a-zA-Z]+)(.*)$')
  26. numeric_regexp = re.compile('^(\d+)(.*)$')
  27. while (s != ''):
  28. if s[0] in digits:
  29. m = numeric_regexp.match(s)
  30. r.append(int(m.group(1)))
  31. s = m.group(2)
  32. continue
  33. if s[0] in ascii_letters:
  34. m = alpha_regexp.match(s)
  35. r.append(m.group(1))
  36. s = m.group(2)
  37. continue
  38. s = s[1:]
  39. return r
  40. def vercmp_part(a, b):
  41. va = explode_version(a)
  42. vb = explode_version(b)
  43. while True:
  44. if va == []:
  45. ca = None
  46. else:
  47. ca = va.pop(0)
  48. if vb == []:
  49. cb = None
  50. else:
  51. cb = vb.pop(0)
  52. if ca == None and cb == None:
  53. return 0
  54. if ca > cb:
  55. return 1
  56. if ca < cb:
  57. return -1
  58. def vercmp(ta, tb):
  59. (ea, va, ra) = ta
  60. (eb, vb, rb) = tb
  61. r = int(ea)-int(eb)
  62. if (r == 0):
  63. r = vercmp_part(va, vb)
  64. if (r == 0):
  65. r = vercmp_part(ra, rb)
  66. return r
  67. def explode_deps(s):
  68. """
  69. Take an RDEPENDS style string of format:
  70. "DEPEND1 (optional version) DEPEND2 (optional version) ..."
  71. and return a list of dependencies.
  72. Version information is ignored.
  73. """
  74. r = []
  75. l = s.split()
  76. flag = False
  77. for i in l:
  78. if i[0] == '(':
  79. flag = True
  80. #j = []
  81. if not flag:
  82. r.append(i)
  83. #else:
  84. # j.append(i)
  85. if flag and i.endswith(')'):
  86. flag = False
  87. # Ignore version
  88. #r[-1] += ' ' + ' '.join(j)
  89. return r
  90. def explode_dep_versions(s):
  91. """
  92. Take an RDEPENDS style string of format:
  93. "DEPEND1 (optional version) DEPEND2 (optional version) ..."
  94. and return a dictonary of dependencies and versions.
  95. """
  96. r = {}
  97. l = s.split()
  98. lastdep = None
  99. lastver = ""
  100. inversion = False
  101. for i in l:
  102. if i[0] == '(':
  103. inversion = True
  104. lastver = i[1:] or ""
  105. #j = []
  106. elif inversion and i.endswith(')'):
  107. inversion = False
  108. lastver = lastver + " " + (i[:-1] or "")
  109. r[lastdep] = lastver
  110. elif not inversion:
  111. r[i] = None
  112. lastdep = i
  113. lastver = ""
  114. elif inversion:
  115. lastver = lastver + " " + i
  116. return r
  117. def _print_trace(body, line):
  118. """
  119. Print the Environment of a Text Body
  120. """
  121. import bb
  122. # print the environment of the method
  123. bb.msg.error(bb.msg.domain.Util, "Printing the environment of the function")
  124. min_line = max(1,line-4)
  125. max_line = min(line+4,len(body)-1)
  126. for i in range(min_line,max_line+1):
  127. bb.msg.error(bb.msg.domain.Util, "\t%.4d:%s" % (i, body[i-1]) )
  128. def better_compile(text, file, realfile):
  129. """
  130. A better compile method. This method
  131. will print the offending lines.
  132. """
  133. try:
  134. return compile(text, file, "exec")
  135. except Exception, e:
  136. import bb,sys
  137. # split the text into lines again
  138. body = text.split('\n')
  139. bb.msg.error(bb.msg.domain.Util, "Error in compiling python function in: ", realfile)
  140. bb.msg.error(bb.msg.domain.Util, "The lines resulting into this error were:")
  141. bb.msg.error(bb.msg.domain.Util, "\t%d:%s:'%s'" % (e.lineno, e.__class__.__name__, body[e.lineno-1]))
  142. _print_trace(body, e.lineno)
  143. # exit now
  144. sys.exit(1)
  145. def better_exec(code, context, text, realfile):
  146. """
  147. Similiar to better_compile, better_exec will
  148. print the lines that are responsible for the
  149. error.
  150. """
  151. import bb,sys
  152. try:
  153. exec code in context
  154. except:
  155. (t,value,tb) = sys.exc_info()
  156. if t in [bb.parse.SkipPackage, bb.build.FuncFailed]:
  157. raise
  158. # print the Header of the Error Message
  159. bb.msg.error(bb.msg.domain.Util, "Error in executing python function in: ", realfile)
  160. bb.msg.error(bb.msg.domain.Util, "Exception:%s Message:%s" % (t,value) )
  161. # let us find the line number now
  162. while tb.tb_next:
  163. tb = tb.tb_next
  164. import traceback
  165. line = traceback.tb_lineno(tb)
  166. _print_trace( text.split('\n'), line )
  167. raise
  168. def Enum(*names):
  169. """
  170. A simple class to give Enum support
  171. """
  172. assert names, "Empty enums are not supported"
  173. class EnumClass(object):
  174. __slots__ = names
  175. def __iter__(self): return iter(constants)
  176. def __len__(self): return len(constants)
  177. def __getitem__(self, i): return constants[i]
  178. def __repr__(self): return 'Enum' + str(names)
  179. def __str__(self): return 'enum ' + str(constants)
  180. class EnumValue(object):
  181. __slots__ = ('__value')
  182. def __init__(self, value): self.__value = value
  183. Value = property(lambda self: self.__value)
  184. EnumType = property(lambda self: EnumType)
  185. def __hash__(self): return hash(self.__value)
  186. def __cmp__(self, other):
  187. # C fans might want to remove the following assertion
  188. # to make all enums comparable by ordinal value {;))
  189. assert self.EnumType is other.EnumType, "Only values from the same enum are comparable"
  190. return cmp(self.__value, other.__value)
  191. def __invert__(self): return constants[maximum - self.__value]
  192. def __nonzero__(self): return bool(self.__value)
  193. def __repr__(self): return str(names[self.__value])
  194. maximum = len(names) - 1
  195. constants = [None] * len(names)
  196. for i, each in enumerate(names):
  197. val = EnumValue(i)
  198. setattr(EnumClass, each, val)
  199. constants[i] = val
  200. constants = tuple(constants)
  201. EnumType = EnumClass()
  202. return EnumType
  203. def lockfile(name):
  204. """
  205. Use the file fn as a lock file, return when the lock has been acquired.
  206. Returns a variable to pass to unlockfile().
  207. """
  208. while True:
  209. # If we leave the lockfiles lying around there is no problem
  210. # but we should clean up after ourselves. This gives potential
  211. # for races though. To work around this, when we acquire the lock
  212. # we check the file we locked was still the lock file on disk.
  213. # by comparing inode numbers. If they don't match or the lockfile
  214. # no longer exists, we start again.
  215. # This implementation is unfair since the last person to request the
  216. # lock is the most likely to win it.
  217. lf = open(name, "a+")
  218. fcntl.flock(lf.fileno(), fcntl.LOCK_EX)
  219. statinfo = os.fstat(lf.fileno())
  220. if os.path.exists(lf.name):
  221. statinfo2 = os.stat(lf.name)
  222. if statinfo.st_ino == statinfo2.st_ino:
  223. return lf
  224. # File no longer exists or changed, retry
  225. lf.close
  226. def unlockfile(lf):
  227. """
  228. Unlock a file locked using lockfile()
  229. """
  230. os.unlink(lf.name)
  231. fcntl.flock(lf.fileno(), fcntl.LOCK_UN)
  232. lf.close
  233. def md5_file(filename):
  234. """
  235. Return the hex string representation of the MD5 checksum of filename.
  236. """
  237. try:
  238. import hashlib
  239. m = hashlib.md5()
  240. except ImportError:
  241. import md5
  242. m = md5.new()
  243. for line in open(filename):
  244. m.update(line)
  245. return m.hexdigest()
  246. def sha256_file(filename):
  247. """
  248. Return the hex string representation of the 256-bit SHA checksum of
  249. filename. On Python 2.4 this will return None, so callers will need to
  250. handle that by either skipping SHA checks, or running a standalone sha256sum
  251. binary.
  252. """
  253. try:
  254. import hashlib
  255. except ImportError:
  256. return None
  257. s = hashlib.sha256()
  258. for line in open(filename):
  259. s.update(line)
  260. return s.hexdigest()
  261. def prunedir(topdir):
  262. # Delete everything reachable from the directory named in 'topdir'.
  263. # CAUTION: This is dangerous!
  264. for root, dirs, files in os.walk(topdir, topdown=False):
  265. for name in files:
  266. os.remove(os.path.join(root, name))
  267. for name in dirs:
  268. os.rmdir(os.path.join(root, name))
  269. os.rmdir(topdir)