summaryrefslogtreecommitdiffstats
path: root/bitbake/lib/bb/utils.py
diff options
context:
space:
mode:
Diffstat (limited to 'bitbake/lib/bb/utils.py')
-rw-r--r--bitbake/lib/bb/utils.py396
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
22import re, fcntl, os, string, stat, shutil, time
23import sys
24import bb
25import errno
26import bb.msg
27from commands import getstatusoutput
28
29# Version comparison
22separators = ".-" 30separators = ".-"
23 31
24import re, fcntl, os, types, bb, string, stat, shutil 32# Context used in better_exec, eval
25from commands import getstatusoutput 33_context = {
34 "os": os,
35 "bb": bb,
36 "time": time,
37}
26 38
27def explode_version(s): 39def 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
103def 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
148def 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
88def explode_deps(s): 225def 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
167def better_compile(text, file, realfile): 302def 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
218def Enum(*names): 351def simple_exec(code, context):
219 """ 352 exec(code, _context, context)
220 A simple class to give Enum support 353
221 """ 354def 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
258def lockfile(name): 357def 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
293def unlockfile(lf): 391def 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):
419def prunedir(topdir): 515def 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#
436def prune_suffix(var, suffixes, d): 532def 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
456import stat
457 552
458def movefile(src,dest,newmtime=None,sstat=None): 553def 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
546def copyfile(src,dest,newmtime=None,sstat=None): 640def 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
614def which(path, item, direction = 0): 708def which(path, item, direction = 0):