diff options
Diffstat (limited to 'bitbake/lib/bb/utils.py')
| -rw-r--r-- | bitbake/lib/bb/utils.py | 396 |
1 files changed, 245 insertions, 151 deletions
diff --git a/bitbake/lib/bb/utils.py b/bitbake/lib/bb/utils.py index 86b9c724ed..c0cc9c6ea2 100644 --- a/bitbake/lib/bb/utils.py +++ b/bitbake/lib/bb/utils.py | |||
| @@ -19,10 +19,22 @@ BitBake Utility Functions | |||
| 19 | # with this program; if not, write to the Free Software Foundation, Inc., | 19 | # with this program; if not, write to the Free Software Foundation, Inc., |
| 20 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | 20 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
| 21 | 21 | ||
| 22 | import re, fcntl, os, string, stat, shutil, time | ||
| 23 | import sys | ||
| 24 | import bb | ||
| 25 | import errno | ||
| 26 | import bb.msg | ||
| 27 | from commands import getstatusoutput | ||
| 28 | |||
| 29 | # Version comparison | ||
| 22 | separators = ".-" | 30 | separators = ".-" |
| 23 | 31 | ||
| 24 | import re, fcntl, os, types, bb, string, stat, shutil | 32 | # Context used in better_exec, eval |
| 25 | from commands import getstatusoutput | 33 | _context = { |
| 34 | "os": os, | ||
| 35 | "bb": bb, | ||
| 36 | "time": time, | ||
| 37 | } | ||
| 26 | 38 | ||
| 27 | def explode_version(s): | 39 | def explode_version(s): |
| 28 | r = [] | 40 | r = [] |
| @@ -60,9 +72,9 @@ def vercmp_part(a, b): | |||
| 60 | if ca == None and cb == None: | 72 | if ca == None and cb == None: |
| 61 | return 0 | 73 | return 0 |
| 62 | 74 | ||
| 63 | if type(ca) is types.StringType: | 75 | if isinstance(ca, basestring): |
| 64 | sa = ca in separators | 76 | sa = ca in separators |
| 65 | if type(cb) is types.StringType: | 77 | if isinstance(cb, basestring): |
| 66 | sb = cb in separators | 78 | sb = cb in separators |
| 67 | if sa and not sb: | 79 | if sa and not sb: |
| 68 | return -1 | 80 | return -1 |
| @@ -85,6 +97,131 @@ def vercmp(ta, tb): | |||
| 85 | r = vercmp_part(ra, rb) | 97 | r = vercmp_part(ra, rb) |
| 86 | return r | 98 | return r |
| 87 | 99 | ||
| 100 | _package_weights_ = {"pre":-2, "p":0, "alpha":-4, "beta":-3, "rc":-1} # dicts are unordered | ||
| 101 | _package_ends_ = ["pre", "p", "alpha", "beta", "rc", "cvs", "bk", "HEAD" ] # so we need ordered list | ||
| 102 | |||
| 103 | def relparse(myver): | ||
| 104 | """Parses the last elements of a version number into a triplet, that can | ||
| 105 | later be compared. | ||
| 106 | """ | ||
| 107 | |||
| 108 | number = 0 | ||
| 109 | p1 = 0 | ||
| 110 | p2 = 0 | ||
| 111 | mynewver = myver.split('_') | ||
| 112 | if len(mynewver) == 2: | ||
| 113 | # an _package_weights_ | ||
| 114 | number = float(mynewver[0]) | ||
| 115 | match = 0 | ||
| 116 | for x in _package_ends_: | ||
| 117 | elen = len(x) | ||
| 118 | if mynewver[1][:elen] == x: | ||
| 119 | match = 1 | ||
| 120 | p1 = _package_weights_[x] | ||
| 121 | try: | ||
| 122 | p2 = float(mynewver[1][elen:]) | ||
| 123 | except: | ||
| 124 | p2 = 0 | ||
| 125 | break | ||
| 126 | if not match: | ||
| 127 | # normal number or number with letter at end | ||
| 128 | divider = len(myver)-1 | ||
| 129 | if myver[divider:] not in "1234567890": | ||
| 130 | # letter at end | ||
| 131 | p1 = ord(myver[divider:]) | ||
| 132 | number = float(myver[0:divider]) | ||
| 133 | else: | ||
| 134 | number = float(myver) | ||
| 135 | else: | ||
| 136 | # normal number or number with letter at end | ||
| 137 | divider = len(myver)-1 | ||
| 138 | if myver[divider:] not in "1234567890": | ||
| 139 | #letter at end | ||
| 140 | p1 = ord(myver[divider:]) | ||
| 141 | number = float(myver[0:divider]) | ||
| 142 | else: | ||
| 143 | number = float(myver) | ||
| 144 | return [number, p1, p2] | ||
| 145 | |||
| 146 | __vercmp_cache__ = {} | ||
| 147 | |||
| 148 | def vercmp_string(val1, val2): | ||
| 149 | """This takes two version strings and returns an integer to tell you whether | ||
| 150 | the versions are the same, val1>val2 or val2>val1. | ||
| 151 | """ | ||
| 152 | |||
| 153 | # quick short-circuit | ||
| 154 | if val1 == val2: | ||
| 155 | return 0 | ||
| 156 | valkey = val1 + " " + val2 | ||
| 157 | |||
| 158 | # cache lookup | ||
| 159 | try: | ||
| 160 | return __vercmp_cache__[valkey] | ||
| 161 | try: | ||
| 162 | return - __vercmp_cache__[val2 + " " + val1] | ||
| 163 | except KeyError: | ||
| 164 | pass | ||
| 165 | except KeyError: | ||
| 166 | pass | ||
| 167 | |||
| 168 | # consider 1_p2 vc 1.1 | ||
| 169 | # after expansion will become (1_p2,0) vc (1,1) | ||
| 170 | # then 1_p2 is compared with 1 before 0 is compared with 1 | ||
| 171 | # to solve the bug we need to convert it to (1,0_p2) | ||
| 172 | # by splitting _prepart part and adding it back _after_expansion | ||
| 173 | |||
| 174 | val1_prepart = val2_prepart = '' | ||
| 175 | if val1.count('_'): | ||
| 176 | val1, val1_prepart = val1.split('_', 1) | ||
| 177 | if val2.count('_'): | ||
| 178 | val2, val2_prepart = val2.split('_', 1) | ||
| 179 | |||
| 180 | # replace '-' by '.' | ||
| 181 | # FIXME: Is it needed? can val1/2 contain '-'? | ||
| 182 | |||
| 183 | val1 = val1.split("-") | ||
| 184 | if len(val1) == 2: | ||
| 185 | val1[0] = val1[0] + "." + val1[1] | ||
| 186 | val2 = val2.split("-") | ||
| 187 | if len(val2) == 2: | ||
| 188 | val2[0] = val2[0] + "." + val2[1] | ||
| 189 | |||
| 190 | val1 = val1[0].split('.') | ||
| 191 | val2 = val2[0].split('.') | ||
| 192 | |||
| 193 | # add back decimal point so that .03 does not become "3" ! | ||
| 194 | for x in range(1, len(val1)): | ||
| 195 | if val1[x][0] == '0' : | ||
| 196 | val1[x] = '.' + val1[x] | ||
| 197 | for x in range(1, len(val2)): | ||
| 198 | if val2[x][0] == '0' : | ||
| 199 | val2[x] = '.' + val2[x] | ||
| 200 | |||
| 201 | # extend varion numbers | ||
| 202 | if len(val2) < len(val1): | ||
| 203 | val2.extend(["0"]*(len(val1)-len(val2))) | ||
| 204 | elif len(val1) < len(val2): | ||
| 205 | val1.extend(["0"]*(len(val2)-len(val1))) | ||
| 206 | |||
| 207 | # add back _prepart tails | ||
| 208 | if val1_prepart: | ||
| 209 | val1[-1] += '_' + val1_prepart | ||
| 210 | if val2_prepart: | ||
| 211 | val2[-1] += '_' + val2_prepart | ||
| 212 | # The above code will extend version numbers out so they | ||
| 213 | # have the same number of digits. | ||
| 214 | for x in range(0, len(val1)): | ||
| 215 | cmp1 = relparse(val1[x]) | ||
| 216 | cmp2 = relparse(val2[x]) | ||
| 217 | for y in range(0, 3): | ||
| 218 | myret = cmp1[y] - cmp2[y] | ||
| 219 | if myret != 0: | ||
| 220 | __vercmp_cache__[valkey] = myret | ||
| 221 | return myret | ||
| 222 | __vercmp_cache__[valkey] = 0 | ||
| 223 | return 0 | ||
| 224 | |||
| 88 | def explode_deps(s): | 225 | def explode_deps(s): |
| 89 | """ | 226 | """ |
| 90 | Take an RDEPENDS style string of format: | 227 | Take an RDEPENDS style string of format: |
| @@ -154,26 +291,22 @@ def _print_trace(body, line): | |||
| 154 | """ | 291 | """ |
| 155 | Print the Environment of a Text Body | 292 | Print the Environment of a Text Body |
| 156 | """ | 293 | """ |
| 157 | import bb | ||
| 158 | |||
| 159 | # print the environment of the method | 294 | # print the environment of the method |
| 160 | bb.msg.error(bb.msg.domain.Util, "Printing the environment of the function") | 295 | bb.msg.error(bb.msg.domain.Util, "Printing the environment of the function") |
| 161 | min_line = max(1,line-4) | 296 | min_line = max(1, line-4) |
| 162 | max_line = min(line+4,len(body)-1) | 297 | max_line = min(line + 4, len(body)-1) |
| 163 | for i in range(min_line,max_line+1): | 298 | for i in range(min_line, max_line + 1): |
| 164 | bb.msg.error(bb.msg.domain.Util, "\t%.4d:%s" % (i, body[i-1]) ) | 299 | bb.msg.error(bb.msg.domain.Util, "\t%.4d:%s" % (i, body[i-1]) ) |
| 165 | 300 | ||
| 166 | 301 | ||
| 167 | def better_compile(text, file, realfile): | 302 | def better_compile(text, file, realfile, mode = "exec"): |
| 168 | """ | 303 | """ |
| 169 | A better compile method. This method | 304 | A better compile method. This method |
| 170 | will print the offending lines. | 305 | will print the offending lines. |
| 171 | """ | 306 | """ |
| 172 | try: | 307 | try: |
| 173 | return compile(text, file, "exec") | 308 | return compile(text, file, mode) |
| 174 | except Exception, e: | 309 | except Exception as e: |
| 175 | import bb,sys | ||
| 176 | |||
| 177 | # split the text into lines again | 310 | # split the text into lines again |
| 178 | body = text.split('\n') | 311 | body = text.split('\n') |
| 179 | bb.msg.error(bb.msg.domain.Util, "Error in compiling python function in: ", realfile) | 312 | bb.msg.error(bb.msg.domain.Util, "Error in compiling python function in: ", realfile) |
| @@ -191,18 +324,18 @@ def better_exec(code, context, text, realfile): | |||
| 191 | print the lines that are responsible for the | 324 | print the lines that are responsible for the |
| 192 | error. | 325 | error. |
| 193 | """ | 326 | """ |
| 194 | import bb,sys | 327 | import bb.parse |
| 195 | try: | 328 | try: |
| 196 | exec code in context | 329 | exec(code, _context, context) |
| 197 | except: | 330 | except: |
| 198 | (t,value,tb) = sys.exc_info() | 331 | (t, value, tb) = sys.exc_info() |
| 199 | 332 | ||
| 200 | if t in [bb.parse.SkipPackage, bb.build.FuncFailed]: | 333 | if t in [bb.parse.SkipPackage, bb.build.FuncFailed]: |
| 201 | raise | 334 | raise |
| 202 | 335 | ||
| 203 | # print the Header of the Error Message | 336 | # print the Header of the Error Message |
| 204 | bb.msg.error(bb.msg.domain.Util, "Error in executing python function in: %s" % realfile) | 337 | bb.msg.error(bb.msg.domain.Util, "Error in executing python function in: %s" % realfile) |
| 205 | bb.msg.error(bb.msg.domain.Util, "Exception:%s Message:%s" % (t,value) ) | 338 | bb.msg.error(bb.msg.domain.Util, "Exception:%s Message:%s" % (t, value)) |
| 206 | 339 | ||
| 207 | # let us find the line number now | 340 | # let us find the line number now |
| 208 | while tb.tb_next: | 341 | while tb.tb_next: |
| @@ -212,48 +345,14 @@ def better_exec(code, context, text, realfile): | |||
| 212 | line = traceback.tb_lineno(tb) | 345 | line = traceback.tb_lineno(tb) |
| 213 | 346 | ||
| 214 | _print_trace( text.split('\n'), line ) | 347 | _print_trace( text.split('\n'), line ) |
| 215 | 348 | ||
| 216 | raise | 349 | raise |
| 217 | 350 | ||
| 218 | def Enum(*names): | 351 | def simple_exec(code, context): |
| 219 | """ | 352 | exec(code, _context, context) |
| 220 | A simple class to give Enum support | 353 | |
| 221 | """ | 354 | def better_eval(source, locals): |
| 222 | 355 | return eval(source, _context, locals) | |
| 223 | assert names, "Empty enums are not supported" | ||
| 224 | |||
| 225 | class EnumClass(object): | ||
| 226 | __slots__ = names | ||
| 227 | def __iter__(self): return iter(constants) | ||
| 228 | def __len__(self): return len(constants) | ||
| 229 | def __getitem__(self, i): return constants[i] | ||
| 230 | def __repr__(self): return 'Enum' + str(names) | ||
| 231 | def __str__(self): return 'enum ' + str(constants) | ||
| 232 | |||
| 233 | class EnumValue(object): | ||
| 234 | __slots__ = ('__value') | ||
| 235 | def __init__(self, value): self.__value = value | ||
| 236 | Value = property(lambda self: self.__value) | ||
| 237 | EnumType = property(lambda self: EnumType) | ||
| 238 | def __hash__(self): return hash(self.__value) | ||
| 239 | def __cmp__(self, other): | ||
| 240 | # C fans might want to remove the following assertion | ||
| 241 | # to make all enums comparable by ordinal value {;)) | ||
| 242 | assert self.EnumType is other.EnumType, "Only values from the same enum are comparable" | ||
| 243 | return cmp(self.__value, other.__value) | ||
| 244 | def __invert__(self): return constants[maximum - self.__value] | ||
| 245 | def __nonzero__(self): return bool(self.__value) | ||
| 246 | def __repr__(self): return str(names[self.__value]) | ||
| 247 | |||
| 248 | maximum = len(names) - 1 | ||
| 249 | constants = [None] * len(names) | ||
| 250 | for i, each in enumerate(names): | ||
| 251 | val = EnumValue(i) | ||
| 252 | setattr(EnumClass, each, val) | ||
| 253 | constants[i] = val | ||
| 254 | constants = tuple(constants) | ||
| 255 | EnumType = EnumClass() | ||
| 256 | return EnumType | ||
| 257 | 356 | ||
| 258 | def lockfile(name): | 357 | def lockfile(name): |
| 259 | """ | 358 | """ |
| @@ -262,37 +361,36 @@ def lockfile(name): | |||
| 262 | """ | 361 | """ |
| 263 | path = os.path.dirname(name) | 362 | path = os.path.dirname(name) |
| 264 | if not os.path.isdir(path): | 363 | if not os.path.isdir(path): |
| 265 | import bb, sys | ||
| 266 | bb.msg.error(bb.msg.domain.Util, "Error, lockfile path does not exist!: %s" % path) | 364 | bb.msg.error(bb.msg.domain.Util, "Error, lockfile path does not exist!: %s" % path) |
| 267 | sys.exit(1) | 365 | sys.exit(1) |
| 268 | 366 | ||
| 269 | while True: | 367 | while True: |
| 270 | # If we leave the lockfiles lying around there is no problem | 368 | # If we leave the lockfiles lying around there is no problem |
| 271 | # but we should clean up after ourselves. This gives potential | 369 | # but we should clean up after ourselves. This gives potential |
| 272 | # for races though. To work around this, when we acquire the lock | 370 | # for races though. To work around this, when we acquire the lock |
| 273 | # we check the file we locked was still the lock file on disk. | 371 | # we check the file we locked was still the lock file on disk. |
| 274 | # by comparing inode numbers. If they don't match or the lockfile | 372 | # by comparing inode numbers. If they don't match or the lockfile |
| 275 | # no longer exists, we start again. | 373 | # no longer exists, we start again. |
| 276 | 374 | ||
| 277 | # This implementation is unfair since the last person to request the | 375 | # This implementation is unfair since the last person to request the |
| 278 | # lock is the most likely to win it. | 376 | # lock is the most likely to win it. |
| 279 | 377 | ||
| 280 | try: | 378 | try: |
| 281 | lf = open(name, "a+") | 379 | lf = open(name, "a + ") |
| 282 | fcntl.flock(lf.fileno(), fcntl.LOCK_EX) | 380 | fcntl.flock(lf.fileno(), fcntl.LOCK_EX) |
| 283 | statinfo = os.fstat(lf.fileno()) | 381 | statinfo = os.fstat(lf.fileno()) |
| 284 | if os.path.exists(lf.name): | 382 | if os.path.exists(lf.name): |
| 285 | statinfo2 = os.stat(lf.name) | 383 | statinfo2 = os.stat(lf.name) |
| 286 | if statinfo.st_ino == statinfo2.st_ino: | 384 | if statinfo.st_ino == statinfo2.st_ino: |
| 287 | return lf | 385 | return lf |
| 288 | # File no longer exists or changed, retry | 386 | # File no longer exists or changed, retry |
| 289 | lf.close | 387 | lf.close |
| 290 | except Exception, e: | 388 | except Exception as e: |
| 291 | continue | 389 | continue |
| 292 | 390 | ||
| 293 | def unlockfile(lf): | 391 | def unlockfile(lf): |
| 294 | """ | 392 | """ |
| 295 | Unlock a file locked using lockfile() | 393 | Unlock a file locked using lockfile() |
| 296 | """ | 394 | """ |
| 297 | os.unlink(lf.name) | 395 | os.unlink(lf.name) |
| 298 | fcntl.flock(lf.fileno(), fcntl.LOCK_UN) | 396 | fcntl.flock(lf.fileno(), fcntl.LOCK_UN) |
| @@ -308,7 +406,7 @@ def md5_file(filename): | |||
| 308 | except ImportError: | 406 | except ImportError: |
| 309 | import md5 | 407 | import md5 |
| 310 | m = md5.new() | 408 | m = md5.new() |
| 311 | 409 | ||
| 312 | for line in open(filename): | 410 | for line in open(filename): |
| 313 | m.update(line) | 411 | m.update(line) |
| 314 | return m.hexdigest() | 412 | return m.hexdigest() |
| @@ -368,19 +466,17 @@ def filter_environment(good_vars): | |||
| 368 | are not known and may influence the build in a negative way. | 466 | are not known and may influence the build in a negative way. |
| 369 | """ | 467 | """ |
| 370 | 468 | ||
| 371 | import bb | ||
| 372 | |||
| 373 | removed_vars = [] | 469 | removed_vars = [] |
| 374 | for key in os.environ.keys(): | 470 | for key in os.environ.keys(): |
| 375 | if key in good_vars: | 471 | if key in good_vars: |
| 376 | continue | 472 | continue |
| 377 | 473 | ||
| 378 | removed_vars.append(key) | 474 | removed_vars.append(key) |
| 379 | os.unsetenv(key) | 475 | os.unsetenv(key) |
| 380 | del os.environ[key] | 476 | del os.environ[key] |
| 381 | 477 | ||
| 382 | if len(removed_vars): | 478 | if len(removed_vars): |
| 383 | bb.debug(1, "Removed the following variables from the environment:", ",".join(removed_vars)) | 479 | bb.msg.debug(1, bb.msg.domain.Util, "Removed the following variables from the environment:", ",".join(removed_vars)) |
| 384 | 480 | ||
| 385 | return removed_vars | 481 | return removed_vars |
| 386 | 482 | ||
| @@ -410,7 +506,7 @@ def build_environment(d): | |||
| 410 | """ | 506 | """ |
| 411 | Build an environment from all exported variables. | 507 | Build an environment from all exported variables. |
| 412 | """ | 508 | """ |
| 413 | import bb | 509 | import bb.data |
| 414 | for var in bb.data.keys(d): | 510 | for var in bb.data.keys(d): |
| 415 | export = bb.data.getVarFlag(var, "export", d) | 511 | export = bb.data.getVarFlag(var, "export", d) |
| 416 | if export: | 512 | if export: |
| @@ -419,7 +515,7 @@ def build_environment(d): | |||
| 419 | def prunedir(topdir): | 515 | def prunedir(topdir): |
| 420 | # Delete everything reachable from the directory named in 'topdir'. | 516 | # Delete everything reachable from the directory named in 'topdir'. |
| 421 | # CAUTION: This is dangerous! | 517 | # CAUTION: This is dangerous! |
| 422 | for root, dirs, files in os.walk(topdir, topdown=False): | 518 | for root, dirs, files in os.walk(topdir, topdown = False): |
| 423 | for name in files: | 519 | for name in files: |
| 424 | os.remove(os.path.join(root, name)) | 520 | os.remove(os.path.join(root, name)) |
| 425 | for name in dirs: | 521 | for name in dirs: |
| @@ -434,7 +530,7 @@ def prunedir(topdir): | |||
| 434 | # but thats possibly insane and suffixes is probably going to be small | 530 | # but thats possibly insane and suffixes is probably going to be small |
| 435 | # | 531 | # |
| 436 | def prune_suffix(var, suffixes, d): | 532 | def prune_suffix(var, suffixes, d): |
| 437 | # See if var ends with any of the suffixes listed and | 533 | # See if var ends with any of the suffixes listed and |
| 438 | # remove it if found | 534 | # remove it if found |
| 439 | for suffix in suffixes: | 535 | for suffix in suffixes: |
| 440 | if var.endswith(suffix): | 536 | if var.endswith(suffix): |
| @@ -446,169 +542,167 @@ def mkdirhier(dir): | |||
| 446 | directory already exists like os.makedirs | 542 | directory already exists like os.makedirs |
| 447 | """ | 543 | """ |
| 448 | 544 | ||
| 449 | bb.debug(3, "mkdirhier(%s)" % dir) | 545 | bb.msg.debug(3, bb.msg.domain.Util, "mkdirhier(%s)" % dir) |
| 450 | try: | 546 | try: |
| 451 | os.makedirs(dir) | 547 | os.makedirs(dir) |
| 452 | bb.debug(2, "created " + dir) | 548 | bb.msg.debug(2, bb.msg.domain.Util, "created " + dir) |
| 453 | except OSError, e: | 549 | except OSError as e: |
| 454 | if e.errno != 17: raise e | 550 | if e.errno != errno.EEXIST: |
| 455 | 551 | raise e | |
| 456 | import stat | ||
| 457 | 552 | ||
| 458 | def movefile(src,dest,newmtime=None,sstat=None): | 553 | def movefile(src, dest, newmtime = None, sstat = None): |
| 459 | """Moves a file from src to dest, preserving all permissions and | 554 | """Moves a file from src to dest, preserving all permissions and |
| 460 | attributes; mtime will be preserved even when moving across | 555 | attributes; mtime will be preserved even when moving across |
| 461 | filesystems. Returns true on success and false on failure. Move is | 556 | filesystems. Returns true on success and false on failure. Move is |
| 462 | atomic. | 557 | atomic. |
| 463 | """ | 558 | """ |
| 464 | 559 | ||
| 465 | #print "movefile("+src+","+dest+","+str(newmtime)+","+str(sstat)+")" | 560 | #print "movefile(" + src + "," + dest + "," + str(newmtime) + "," + str(sstat) + ")" |
| 466 | try: | 561 | try: |
| 467 | if not sstat: | 562 | if not sstat: |
| 468 | sstat=os.lstat(src) | 563 | sstat = os.lstat(src) |
| 469 | except Exception, e: | 564 | except Exception as e: |
| 470 | print "movefile: Stating source file failed...", e | 565 | print("movefile: Stating source file failed...", e) |
| 471 | return None | 566 | return None |
| 472 | 567 | ||
| 473 | destexists=1 | 568 | destexists = 1 |
| 474 | try: | 569 | try: |
| 475 | dstat=os.lstat(dest) | 570 | dstat = os.lstat(dest) |
| 476 | except: | 571 | except: |
| 477 | dstat=os.lstat(os.path.dirname(dest)) | 572 | dstat = os.lstat(os.path.dirname(dest)) |
| 478 | destexists=0 | 573 | destexists = 0 |
| 479 | 574 | ||
| 480 | if destexists: | 575 | if destexists: |
| 481 | if stat.S_ISLNK(dstat[stat.ST_MODE]): | 576 | if stat.S_ISLNK(dstat[stat.ST_MODE]): |
| 482 | try: | 577 | try: |
| 483 | os.unlink(dest) | 578 | os.unlink(dest) |
| 484 | destexists=0 | 579 | destexists = 0 |
| 485 | except Exception, e: | 580 | except Exception as e: |
| 486 | pass | 581 | pass |
| 487 | 582 | ||
| 488 | if stat.S_ISLNK(sstat[stat.ST_MODE]): | 583 | if stat.S_ISLNK(sstat[stat.ST_MODE]): |
| 489 | try: | 584 | try: |
| 490 | target=os.readlink(src) | 585 | target = os.readlink(src) |
| 491 | if destexists and not stat.S_ISDIR(dstat[stat.ST_MODE]): | 586 | if destexists and not stat.S_ISDIR(dstat[stat.ST_MODE]): |
| 492 | os.unlink(dest) | 587 | os.unlink(dest) |
| 493 | os.symlink(target,dest) | 588 | os.symlink(target, dest) |
| 494 | #os.lchown(dest,sstat[stat.ST_UID],sstat[stat.ST_GID]) | 589 | #os.lchown(dest,sstat[stat.ST_UID],sstat[stat.ST_GID]) |
| 495 | os.unlink(src) | 590 | os.unlink(src) |
| 496 | return os.lstat(dest) | 591 | return os.lstat(dest) |
| 497 | except Exception, e: | 592 | except Exception as e: |
| 498 | print "movefile: failed to properly create symlink:", dest, "->", target, e | 593 | print("movefile: failed to properly create symlink:", dest, "->", target, e) |
| 499 | return None | 594 | return None |
| 500 | 595 | ||
| 501 | renamefailed=1 | 596 | renamefailed = 1 |
| 502 | if sstat[stat.ST_DEV]==dstat[stat.ST_DEV]: | 597 | if sstat[stat.ST_DEV] == dstat[stat.ST_DEV]: |
| 503 | try: | 598 | try: |
| 504 | ret=os.rename(src,dest) | 599 | os.rename(src, dest) |
| 505 | renamefailed=0 | 600 | renamefailed = 0 |
| 506 | except Exception, e: | 601 | except Exception as e: |
| 507 | import errno | 602 | if e[0] != errno.EXDEV: |
| 508 | if e[0]!=errno.EXDEV: | ||
| 509 | # Some random error. | 603 | # Some random error. |
| 510 | print "movefile: Failed to move", src, "to", dest, e | 604 | print("movefile: Failed to move", src, "to", dest, e) |
| 511 | return None | 605 | return None |
| 512 | # Invalid cross-device-link 'bind' mounted or actually Cross-Device | 606 | # Invalid cross-device-link 'bind' mounted or actually Cross-Device |
| 513 | 607 | ||
| 514 | if renamefailed: | 608 | if renamefailed: |
| 515 | didcopy=0 | 609 | didcopy = 0 |
| 516 | if stat.S_ISREG(sstat[stat.ST_MODE]): | 610 | if stat.S_ISREG(sstat[stat.ST_MODE]): |
| 517 | try: # For safety copy then move it over. | 611 | try: # For safety copy then move it over. |
| 518 | shutil.copyfile(src,dest+"#new") | 612 | shutil.copyfile(src, dest + "#new") |
| 519 | os.rename(dest+"#new",dest) | 613 | os.rename(dest + "#new", dest) |
| 520 | didcopy=1 | 614 | didcopy = 1 |
| 521 | except Exception, e: | 615 | except Exception as e: |
| 522 | print 'movefile: copy', src, '->', dest, 'failed.', e | 616 | print('movefile: copy', src, '->', dest, 'failed.', e) |
| 523 | return None | 617 | return None |
| 524 | else: | 618 | else: |
| 525 | #we don't yet handle special, so we need to fall back to /bin/mv | 619 | #we don't yet handle special, so we need to fall back to /bin/mv |
| 526 | a=getstatusoutput("/bin/mv -f "+"'"+src+"' '"+dest+"'") | 620 | a = getstatusoutput("/bin/mv -f " + "'" + src + "' '" + dest + "'") |
| 527 | if a[0]!=0: | 621 | if a[0] != 0: |
| 528 | print "movefile: Failed to move special file:" + src + "' to '" + dest + "'", a | 622 | print("movefile: Failed to move special file:" + src + "' to '" + dest + "'", a) |
| 529 | return None # failure | 623 | return None # failure |
| 530 | try: | 624 | try: |
| 531 | if didcopy: | 625 | if didcopy: |
| 532 | os.lchown(dest,sstat[stat.ST_UID],sstat[stat.ST_GID]) | 626 | os.lchown(dest, sstat[stat.ST_UID], sstat[stat.ST_GID]) |
| 533 | os.chmod(dest, stat.S_IMODE(sstat[stat.ST_MODE])) # Sticky is reset on chown | 627 | os.chmod(dest, stat.S_IMODE(sstat[stat.ST_MODE])) # Sticky is reset on chown |
| 534 | os.unlink(src) | 628 | os.unlink(src) |
| 535 | except Exception, e: | 629 | except Exception as e: |
| 536 | print "movefile: Failed to chown/chmod/unlink", dest, e | 630 | print("movefile: Failed to chown/chmod/unlink", dest, e) |
| 537 | return None | 631 | return None |
| 538 | 632 | ||
| 539 | if newmtime: | 633 | if newmtime: |
| 540 | os.utime(dest,(newmtime,newmtime)) | 634 | os.utime(dest, (newmtime, newmtime)) |
| 541 | else: | 635 | else: |
| 542 | os.utime(dest, (sstat[stat.ST_ATIME], sstat[stat.ST_MTIME])) | 636 | os.utime(dest, (sstat[stat.ST_ATIME], sstat[stat.ST_MTIME])) |
| 543 | newmtime=sstat[stat.ST_MTIME] | 637 | newmtime = sstat[stat.ST_MTIME] |
| 544 | return newmtime | 638 | return newmtime |
| 545 | 639 | ||
| 546 | def copyfile(src,dest,newmtime=None,sstat=None): | 640 | def copyfile(src, dest, newmtime = None, sstat = None): |
| 547 | """ | 641 | """ |
| 548 | Copies a file from src to dest, preserving all permissions and | 642 | Copies a file from src to dest, preserving all permissions and |
| 549 | attributes; mtime will be preserved even when moving across | 643 | attributes; mtime will be preserved even when moving across |
| 550 | filesystems. Returns true on success and false on failure. | 644 | filesystems. Returns true on success and false on failure. |
| 551 | """ | 645 | """ |
| 552 | #print "copyfile("+src+","+dest+","+str(newmtime)+","+str(sstat)+")" | 646 | #print "copyfile(" + src + "," + dest + "," + str(newmtime) + "," + str(sstat) + ")" |
| 553 | try: | 647 | try: |
| 554 | if not sstat: | 648 | if not sstat: |
| 555 | sstat=os.lstat(src) | 649 | sstat = os.lstat(src) |
| 556 | except Exception, e: | 650 | except Exception as e: |
| 557 | print "copyfile: Stating source file failed...", e | 651 | print("copyfile: Stating source file failed...", e) |
| 558 | return False | 652 | return False |
| 559 | 653 | ||
| 560 | destexists=1 | 654 | destexists = 1 |
| 561 | try: | 655 | try: |
| 562 | dstat=os.lstat(dest) | 656 | dstat = os.lstat(dest) |
| 563 | except: | 657 | except: |
| 564 | dstat=os.lstat(os.path.dirname(dest)) | 658 | dstat = os.lstat(os.path.dirname(dest)) |
| 565 | destexists=0 | 659 | destexists = 0 |
| 566 | 660 | ||
| 567 | if destexists: | 661 | if destexists: |
| 568 | if stat.S_ISLNK(dstat[stat.ST_MODE]): | 662 | if stat.S_ISLNK(dstat[stat.ST_MODE]): |
| 569 | try: | 663 | try: |
| 570 | os.unlink(dest) | 664 | os.unlink(dest) |
| 571 | destexists=0 | 665 | destexists = 0 |
| 572 | except Exception, e: | 666 | except Exception as e: |
| 573 | pass | 667 | pass |
| 574 | 668 | ||
| 575 | if stat.S_ISLNK(sstat[stat.ST_MODE]): | 669 | if stat.S_ISLNK(sstat[stat.ST_MODE]): |
| 576 | try: | 670 | try: |
| 577 | target=os.readlink(src) | 671 | target = os.readlink(src) |
| 578 | if destexists and not stat.S_ISDIR(dstat[stat.ST_MODE]): | 672 | if destexists and not stat.S_ISDIR(dstat[stat.ST_MODE]): |
| 579 | os.unlink(dest) | 673 | os.unlink(dest) |
| 580 | os.symlink(target,dest) | 674 | os.symlink(target, dest) |
| 581 | #os.lchown(dest,sstat[stat.ST_UID],sstat[stat.ST_GID]) | 675 | #os.lchown(dest,sstat[stat.ST_UID],sstat[stat.ST_GID]) |
| 582 | return os.lstat(dest) | 676 | return os.lstat(dest) |
| 583 | except Exception, e: | 677 | except Exception as e: |
| 584 | print "copyfile: failed to properly create symlink:", dest, "->", target, e | 678 | print("copyfile: failed to properly create symlink:", dest, "->", target, e) |
| 585 | return False | 679 | return False |
| 586 | 680 | ||
| 587 | if stat.S_ISREG(sstat[stat.ST_MODE]): | 681 | if stat.S_ISREG(sstat[stat.ST_MODE]): |
| 588 | try: # For safety copy then move it over. | 682 | try: # For safety copy then move it over. |
| 589 | shutil.copyfile(src,dest+"#new") | 683 | shutil.copyfile(src, dest + "#new") |
| 590 | os.rename(dest+"#new",dest) | 684 | os.rename(dest + "#new", dest) |
| 591 | except Exception, e: | 685 | except Exception as e: |
| 592 | print 'copyfile: copy', src, '->', dest, 'failed.', e | 686 | print('copyfile: copy', src, '->', dest, 'failed.', e) |
| 593 | return False | 687 | return False |
| 594 | else: | 688 | else: |
| 595 | #we don't yet handle special, so we need to fall back to /bin/mv | 689 | #we don't yet handle special, so we need to fall back to /bin/mv |
| 596 | a=getstatusoutput("/bin/cp -f "+"'"+src+"' '"+dest+"'") | 690 | a = getstatusoutput("/bin/cp -f " + "'" + src + "' '" + dest + "'") |
| 597 | if a[0]!=0: | 691 | if a[0] != 0: |
| 598 | print "copyfile: Failed to copy special file:" + src + "' to '" + dest + "'", a | 692 | print("copyfile: Failed to copy special file:" + src + "' to '" + dest + "'", a) |
| 599 | return False # failure | 693 | return False # failure |
| 600 | try: | 694 | try: |
| 601 | os.lchown(dest,sstat[stat.ST_UID],sstat[stat.ST_GID]) | 695 | os.lchown(dest, sstat[stat.ST_UID], sstat[stat.ST_GID]) |
| 602 | os.chmod(dest, stat.S_IMODE(sstat[stat.ST_MODE])) # Sticky is reset on chown | 696 | os.chmod(dest, stat.S_IMODE(sstat[stat.ST_MODE])) # Sticky is reset on chown |
| 603 | except Exception, e: | 697 | except Exception as e: |
| 604 | print "copyfile: Failed to chown/chmod/unlink", dest, e | 698 | print("copyfile: Failed to chown/chmod/unlink", dest, e) |
| 605 | return False | 699 | return False |
| 606 | 700 | ||
| 607 | if newmtime: | 701 | if newmtime: |
| 608 | os.utime(dest,(newmtime,newmtime)) | 702 | os.utime(dest, (newmtime, newmtime)) |
| 609 | else: | 703 | else: |
| 610 | os.utime(dest, (sstat[stat.ST_ATIME], sstat[stat.ST_MTIME])) | 704 | os.utime(dest, (sstat[stat.ST_ATIME], sstat[stat.ST_MTIME])) |
| 611 | newmtime=sstat[stat.ST_MTIME] | 705 | newmtime = sstat[stat.ST_MTIME] |
| 612 | return newmtime | 706 | return newmtime |
| 613 | 707 | ||
| 614 | def which(path, item, direction = 0): | 708 | def which(path, item, direction = 0): |
