diff options
Diffstat (limited to 'bitbake-dev/lib/bb/utils.py')
| -rw-r--r-- | bitbake-dev/lib/bb/utils.py | 431 |
1 files changed, 0 insertions, 431 deletions
diff --git a/bitbake-dev/lib/bb/utils.py b/bitbake-dev/lib/bb/utils.py deleted file mode 100644 index 5fc1463e67..0000000000 --- a/bitbake-dev/lib/bb/utils.py +++ /dev/null | |||
| @@ -1,431 +0,0 @@ | |||
| 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 | |||
| 7 | # Copyright (C) 2004 Michael Lauer | ||
| 8 | # | ||
| 9 | # This program is free software; you can redistribute it and/or modify | ||
| 10 | # it under the terms of the GNU General Public License version 2 as | ||
| 11 | # published by the Free Software Foundation. | ||
| 12 | # | ||
| 13 | # This program is distributed in the hope that it will be useful, | ||
| 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 16 | # GNU General Public License for more details. | ||
| 17 | # | ||
| 18 | # You should have received a copy of the GNU General Public License along | ||
| 19 | # with this program; if not, write to the Free Software Foundation, Inc., | ||
| 20 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 21 | |||
| 22 | digits = "0123456789" | ||
| 23 | ascii_letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" | ||
| 24 | separators = ".-" | ||
| 25 | |||
| 26 | import re, fcntl, os, types | ||
| 27 | |||
| 28 | def explode_version(s): | ||
| 29 | r = [] | ||
| 30 | alpha_regexp = re.compile('^([a-zA-Z]+)(.*)$') | ||
| 31 | numeric_regexp = re.compile('^(\d+)(.*)$') | ||
| 32 | while (s != ''): | ||
| 33 | if s[0] in digits: | ||
| 34 | m = numeric_regexp.match(s) | ||
| 35 | r.append(int(m.group(1))) | ||
| 36 | s = m.group(2) | ||
| 37 | continue | ||
| 38 | if s[0] in ascii_letters: | ||
| 39 | m = alpha_regexp.match(s) | ||
| 40 | r.append(m.group(1)) | ||
| 41 | s = m.group(2) | ||
| 42 | continue | ||
| 43 | r.append(s[0]) | ||
| 44 | s = s[1:] | ||
| 45 | return r | ||
| 46 | |||
| 47 | def vercmp_part(a, b): | ||
| 48 | va = explode_version(a) | ||
| 49 | vb = explode_version(b) | ||
| 50 | sa = False | ||
| 51 | sb = False | ||
| 52 | while True: | ||
| 53 | if va == []: | ||
| 54 | ca = None | ||
| 55 | else: | ||
| 56 | ca = va.pop(0) | ||
| 57 | if vb == []: | ||
| 58 | cb = None | ||
| 59 | else: | ||
| 60 | cb = vb.pop(0) | ||
| 61 | if ca == None and cb == None: | ||
| 62 | return 0 | ||
| 63 | |||
| 64 | if type(ca) is types.StringType: | ||
| 65 | sa = ca in separators | ||
| 66 | if type(cb) is types.StringType: | ||
| 67 | sb = cb in separators | ||
| 68 | if sa and not sb: | ||
| 69 | return -1 | ||
| 70 | if not sa and sb: | ||
| 71 | return 1 | ||
| 72 | |||
| 73 | if ca > cb: | ||
| 74 | return 1 | ||
| 75 | if ca < cb: | ||
| 76 | return -1 | ||
| 77 | |||
| 78 | def vercmp(ta, tb): | ||
| 79 | (ea, va, ra) = ta | ||
| 80 | (eb, vb, rb) = tb | ||
| 81 | |||
| 82 | r = int(ea)-int(eb) | ||
| 83 | if (r == 0): | ||
| 84 | r = vercmp_part(va, vb) | ||
| 85 | if (r == 0): | ||
| 86 | r = vercmp_part(ra, rb) | ||
| 87 | return r | ||
| 88 | |||
| 89 | def explode_deps(s): | ||
| 90 | """ | ||
| 91 | Take an RDEPENDS style string of format: | ||
| 92 | "DEPEND1 (optional version) DEPEND2 (optional version) ..." | ||
| 93 | and return a list of dependencies. | ||
| 94 | Version information is ignored. | ||
| 95 | """ | ||
| 96 | r = [] | ||
| 97 | l = s.split() | ||
| 98 | flag = False | ||
| 99 | for i in l: | ||
| 100 | if i[0] == '(': | ||
| 101 | flag = True | ||
| 102 | #j = [] | ||
| 103 | if not flag: | ||
| 104 | r.append(i) | ||
| 105 | #else: | ||
| 106 | # j.append(i) | ||
| 107 | if flag and i.endswith(')'): | ||
| 108 | flag = False | ||
| 109 | # Ignore version | ||
| 110 | #r[-1] += ' ' + ' '.join(j) | ||
| 111 | return r | ||
| 112 | |||
| 113 | def explode_dep_versions(s): | ||
| 114 | """ | ||
| 115 | Take an RDEPENDS style string of format: | ||
| 116 | "DEPEND1 (optional version) DEPEND2 (optional version) ..." | ||
| 117 | and return a dictonary of dependencies and versions. | ||
| 118 | """ | ||
| 119 | r = {} | ||
| 120 | l = s.split() | ||
| 121 | lastdep = None | ||
| 122 | lastver = "" | ||
| 123 | inversion = False | ||
| 124 | for i in l: | ||
| 125 | if i[0] == '(': | ||
| 126 | inversion = True | ||
| 127 | lastver = i[1:] or "" | ||
| 128 | #j = [] | ||
| 129 | elif inversion and i.endswith(')'): | ||
| 130 | inversion = False | ||
| 131 | lastver = lastver + " " + (i[:-1] or "") | ||
| 132 | r[lastdep] = lastver | ||
| 133 | elif not inversion: | ||
| 134 | r[i] = None | ||
| 135 | lastdep = i | ||
| 136 | lastver = "" | ||
| 137 | elif inversion: | ||
| 138 | lastver = lastver + " " + i | ||
| 139 | |||
| 140 | return r | ||
| 141 | |||
| 142 | def _print_trace(body, line): | ||
| 143 | """ | ||
| 144 | Print the Environment of a Text Body | ||
| 145 | """ | ||
| 146 | import bb | ||
| 147 | |||
| 148 | # print the environment of the method | ||
| 149 | bb.msg.error(bb.msg.domain.Util, "Printing the environment of the function") | ||
| 150 | min_line = max(1,line-4) | ||
| 151 | max_line = min(line+4,len(body)-1) | ||
| 152 | for i in range(min_line,max_line+1): | ||
| 153 | bb.msg.error(bb.msg.domain.Util, "\t%.4d:%s" % (i, body[i-1]) ) | ||
| 154 | |||
| 155 | |||
| 156 | def better_compile(text, file, realfile): | ||
| 157 | """ | ||
| 158 | A better compile method. This method | ||
| 159 | will print the offending lines. | ||
| 160 | """ | ||
| 161 | try: | ||
| 162 | return compile(text, file, "exec") | ||
| 163 | except Exception, e: | ||
| 164 | import bb,sys | ||
| 165 | |||
| 166 | # split the text into lines again | ||
| 167 | body = text.split('\n') | ||
| 168 | bb.msg.error(bb.msg.domain.Util, "Error in compiling python function in: ", realfile) | ||
| 169 | bb.msg.error(bb.msg.domain.Util, "The lines resulting into this error were:") | ||
| 170 | bb.msg.error(bb.msg.domain.Util, "\t%d:%s:'%s'" % (e.lineno, e.__class__.__name__, body[e.lineno-1])) | ||
| 171 | |||
| 172 | _print_trace(body, e.lineno) | ||
| 173 | |||
| 174 | # exit now | ||
| 175 | sys.exit(1) | ||
| 176 | |||
| 177 | def better_exec(code, context, text, realfile): | ||
| 178 | """ | ||
| 179 | Similiar to better_compile, better_exec will | ||
| 180 | print the lines that are responsible for the | ||
| 181 | error. | ||
| 182 | """ | ||
| 183 | import bb,sys | ||
| 184 | try: | ||
| 185 | exec code in context | ||
| 186 | except: | ||
| 187 | (t,value,tb) = sys.exc_info() | ||
| 188 | |||
| 189 | if t in [bb.parse.SkipPackage, bb.build.FuncFailed]: | ||
| 190 | raise | ||
| 191 | |||
| 192 | # print the Header of the Error Message | ||
| 193 | bb.msg.error(bb.msg.domain.Util, "Error in executing python function in: %s" % realfile) | ||
| 194 | bb.msg.error(bb.msg.domain.Util, "Exception:%s Message:%s" % (t,value) ) | ||
| 195 | |||
| 196 | # let us find the line number now | ||
| 197 | while tb.tb_next: | ||
| 198 | tb = tb.tb_next | ||
| 199 | |||
| 200 | import traceback | ||
| 201 | line = traceback.tb_lineno(tb) | ||
| 202 | |||
| 203 | _print_trace( text.split('\n'), line ) | ||
| 204 | |||
| 205 | raise | ||
| 206 | |||
| 207 | def Enum(*names): | ||
| 208 | """ | ||
| 209 | A simple class to give Enum support | ||
| 210 | """ | ||
| 211 | |||
| 212 | assert names, "Empty enums are not supported" | ||
| 213 | |||
| 214 | class EnumClass(object): | ||
| 215 | __slots__ = names | ||
| 216 | def __iter__(self): return iter(constants) | ||
| 217 | def __len__(self): return len(constants) | ||
| 218 | def __getitem__(self, i): return constants[i] | ||
| 219 | def __repr__(self): return 'Enum' + str(names) | ||
| 220 | def __str__(self): return 'enum ' + str(constants) | ||
| 221 | |||
| 222 | class EnumValue(object): | ||
| 223 | __slots__ = ('__value') | ||
| 224 | def __init__(self, value): self.__value = value | ||
| 225 | Value = property(lambda self: self.__value) | ||
| 226 | EnumType = property(lambda self: EnumType) | ||
| 227 | def __hash__(self): return hash(self.__value) | ||
| 228 | def __cmp__(self, other): | ||
| 229 | # C fans might want to remove the following assertion | ||
| 230 | # to make all enums comparable by ordinal value {;)) | ||
| 231 | assert self.EnumType is other.EnumType, "Only values from the same enum are comparable" | ||
| 232 | return cmp(self.__value, other.__value) | ||
| 233 | def __invert__(self): return constants[maximum - self.__value] | ||
| 234 | def __nonzero__(self): return bool(self.__value) | ||
| 235 | def __repr__(self): return str(names[self.__value]) | ||
| 236 | |||
| 237 | maximum = len(names) - 1 | ||
| 238 | constants = [None] * len(names) | ||
| 239 | for i, each in enumerate(names): | ||
| 240 | val = EnumValue(i) | ||
| 241 | setattr(EnumClass, each, val) | ||
| 242 | constants[i] = val | ||
| 243 | constants = tuple(constants) | ||
| 244 | EnumType = EnumClass() | ||
| 245 | return EnumType | ||
| 246 | |||
| 247 | def lockfile(name): | ||
| 248 | """ | ||
| 249 | Use the file fn as a lock file, return when the lock has been acquired. | ||
| 250 | Returns a variable to pass to unlockfile(). | ||
| 251 | """ | ||
| 252 | path = os.path.dirname(name) | ||
| 253 | if not os.path.isdir(path): | ||
| 254 | import bb, sys | ||
| 255 | bb.msg.error(bb.msg.domain.Util, "Error, lockfile path does not exist!: %s" % path) | ||
| 256 | sys.exit(1) | ||
| 257 | |||
| 258 | while True: | ||
| 259 | # If we leave the lockfiles lying around there is no problem | ||
| 260 | # but we should clean up after ourselves. This gives potential | ||
| 261 | # for races though. To work around this, when we acquire the lock | ||
| 262 | # we check the file we locked was still the lock file on disk. | ||
| 263 | # by comparing inode numbers. If they don't match or the lockfile | ||
| 264 | # no longer exists, we start again. | ||
| 265 | |||
| 266 | # This implementation is unfair since the last person to request the | ||
| 267 | # lock is the most likely to win it. | ||
| 268 | |||
| 269 | try: | ||
| 270 | lf = open(name, "a+") | ||
| 271 | fcntl.flock(lf.fileno(), fcntl.LOCK_EX) | ||
| 272 | statinfo = os.fstat(lf.fileno()) | ||
| 273 | if os.path.exists(lf.name): | ||
| 274 | statinfo2 = os.stat(lf.name) | ||
| 275 | if statinfo.st_ino == statinfo2.st_ino: | ||
| 276 | return lf | ||
| 277 | # File no longer exists or changed, retry | ||
| 278 | lf.close | ||
| 279 | except Exception, e: | ||
| 280 | continue | ||
| 281 | |||
| 282 | def unlockfile(lf): | ||
| 283 | """ | ||
| 284 | Unlock a file locked using lockfile() | ||
| 285 | """ | ||
| 286 | os.unlink(lf.name) | ||
| 287 | fcntl.flock(lf.fileno(), fcntl.LOCK_UN) | ||
| 288 | lf.close | ||
| 289 | |||
| 290 | def md5_file(filename): | ||
| 291 | """ | ||
| 292 | Return the hex string representation of the MD5 checksum of filename. | ||
| 293 | """ | ||
| 294 | try: | ||
| 295 | import hashlib | ||
| 296 | m = hashlib.md5() | ||
| 297 | except ImportError: | ||
| 298 | import md5 | ||
| 299 | m = md5.new() | ||
| 300 | |||
| 301 | for line in open(filename): | ||
| 302 | m.update(line) | ||
| 303 | return m.hexdigest() | ||
| 304 | |||
| 305 | def sha256_file(filename): | ||
| 306 | """ | ||
| 307 | Return the hex string representation of the 256-bit SHA checksum of | ||
| 308 | filename. On Python 2.4 this will return None, so callers will need to | ||
| 309 | handle that by either skipping SHA checks, or running a standalone sha256sum | ||
| 310 | binary. | ||
| 311 | """ | ||
| 312 | try: | ||
| 313 | import hashlib | ||
| 314 | except ImportError: | ||
| 315 | return None | ||
| 316 | |||
| 317 | s = hashlib.sha256() | ||
| 318 | for line in open(filename): | ||
| 319 | s.update(line) | ||
| 320 | return s.hexdigest() | ||
| 321 | |||
| 322 | def preserved_envvars_list(): | ||
| 323 | return [ | ||
| 324 | 'BBPATH', | ||
| 325 | 'BB_PRESERVE_ENV', | ||
| 326 | 'BB_ENV_WHITELIST', | ||
| 327 | 'BB_ENV_EXTRAWHITE', | ||
| 328 | 'COLORTERM', | ||
| 329 | 'DBUS_SESSION_BUS_ADDRESS', | ||
| 330 | 'DESKTOP_SESSION', | ||
| 331 | 'DESKTOP_STARTUP_ID', | ||
| 332 | 'DISPLAY', | ||
| 333 | 'GNOME_KEYRING_PID', | ||
| 334 | 'GNOME_KEYRING_SOCKET', | ||
| 335 | 'GPG_AGENT_INFO', | ||
| 336 | 'GTK_RC_FILES', | ||
| 337 | 'HOME', | ||
| 338 | 'LANG', | ||
| 339 | 'LOGNAME', | ||
| 340 | 'PATH', | ||
| 341 | 'PWD', | ||
| 342 | 'SESSION_MANAGER', | ||
| 343 | 'SHELL', | ||
| 344 | 'SSH_AUTH_SOCK', | ||
| 345 | 'TERM', | ||
| 346 | 'USER', | ||
| 347 | 'USERNAME', | ||
| 348 | '_', | ||
| 349 | 'XAUTHORITY', | ||
| 350 | 'XDG_DATA_DIRS', | ||
| 351 | 'XDG_SESSION_COOKIE', | ||
| 352 | ] | ||
| 353 | |||
| 354 | def filter_environment(good_vars): | ||
| 355 | """ | ||
| 356 | Create a pristine environment for bitbake. This will remove variables that | ||
| 357 | are not known and may influence the build in a negative way. | ||
| 358 | """ | ||
| 359 | |||
| 360 | import bb | ||
| 361 | |||
| 362 | removed_vars = [] | ||
| 363 | for key in os.environ.keys(): | ||
| 364 | if key in good_vars: | ||
| 365 | continue | ||
| 366 | |||
| 367 | removed_vars.append(key) | ||
| 368 | os.unsetenv(key) | ||
| 369 | del os.environ[key] | ||
| 370 | |||
| 371 | if len(removed_vars): | ||
| 372 | bb.debug(1, "Removed the following variables from the environment:", ",".join(removed_vars)) | ||
| 373 | |||
| 374 | return removed_vars | ||
| 375 | |||
| 376 | def clean_environment(): | ||
| 377 | """ | ||
| 378 | Clean up any spurious environment variables. This will remove any | ||
| 379 | variables the user hasn't chose to preserve. | ||
| 380 | """ | ||
| 381 | if 'BB_PRESERVE_ENV' not in os.environ: | ||
| 382 | if 'BB_ENV_WHITELIST' in os.environ: | ||
| 383 | good_vars = os.environ['BB_ENV_WHITELIST'].split() | ||
| 384 | else: | ||
| 385 | good_vars = preserved_envvars_list() | ||
| 386 | if 'BB_ENV_EXTRAWHITE' in os.environ: | ||
| 387 | good_vars.extend(os.environ['BB_ENV_EXTRAWHITE'].split()) | ||
| 388 | filter_environment(good_vars) | ||
| 389 | |||
| 390 | def empty_environment(): | ||
| 391 | """ | ||
| 392 | Remove all variables from the environment. | ||
| 393 | """ | ||
| 394 | for s in os.environ.keys(): | ||
| 395 | os.unsetenv(s) | ||
| 396 | del os.environ[s] | ||
| 397 | |||
| 398 | def build_environment(d): | ||
| 399 | """ | ||
| 400 | Build an environment from all exported variables. | ||
| 401 | """ | ||
| 402 | import bb | ||
| 403 | for var in bb.data.keys(d): | ||
| 404 | export = bb.data.getVarFlag(var, "export", d) | ||
| 405 | if export: | ||
| 406 | os.environ[var] = bb.data.getVar(var, d, True) | ||
| 407 | |||
| 408 | def prunedir(topdir): | ||
| 409 | # Delete everything reachable from the directory named in 'topdir'. | ||
| 410 | # CAUTION: This is dangerous! | ||
| 411 | for root, dirs, files in os.walk(topdir, topdown=False): | ||
| 412 | for name in files: | ||
| 413 | os.remove(os.path.join(root, name)) | ||
| 414 | for name in dirs: | ||
| 415 | if os.path.islink(os.path.join(root, name)): | ||
| 416 | os.remove(os.path.join(root, name)) | ||
| 417 | else: | ||
| 418 | os.rmdir(os.path.join(root, name)) | ||
| 419 | os.rmdir(topdir) | ||
| 420 | |||
| 421 | # | ||
| 422 | # Could also use return re.compile("(%s)" % "|".join(map(re.escape, suffixes))).sub(lambda mo: "", var) | ||
| 423 | # but thats possibly insane and suffixes is probably going to be small | ||
| 424 | # | ||
| 425 | def prune_suffix(var, suffixes, d): | ||
| 426 | # See if var ends with any of the suffixes listed and | ||
| 427 | # remove it if found | ||
| 428 | for suffix in suffixes: | ||
| 429 | if var.endswith(suffix): | ||
| 430 | return var.replace(suffix, "") | ||
| 431 | return var | ||
