path.py 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. import errno
  2. import glob
  3. import shutil
  4. import subprocess
  5. def join(*paths):
  6. """Like os.path.join but doesn't treat absolute RHS specially"""
  7. return os.path.normpath("/".join(paths))
  8. def relative(src, dest):
  9. """ Return a relative path from src to dest.
  10. >>> relative("/usr/bin", "/tmp/foo/bar")
  11. ../../tmp/foo/bar
  12. >>> relative("/usr/bin", "/usr/lib")
  13. ../lib
  14. >>> relative("/tmp", "/tmp/foo/bar")
  15. foo/bar
  16. """
  17. if hasattr(os.path, "relpath"):
  18. return os.path.relpath(dest, src)
  19. else:
  20. destlist = os.path.normpath(dest).split(os.path.sep)
  21. srclist = os.path.normpath(src).split(os.path.sep)
  22. # Find common section of the path
  23. common = os.path.commonprefix([destlist, srclist])
  24. commonlen = len(common)
  25. # Climb back to the point where they differentiate
  26. relpath = [ os.path.pardir ] * (len(srclist) - commonlen)
  27. if commonlen < len(destlist):
  28. # Add remaining portion
  29. relpath += destlist[commonlen:]
  30. return os.path.sep.join(relpath)
  31. def make_relative_symlink(path):
  32. """ Convert an absolute symlink to a relative one """
  33. if not os.path.islink(path):
  34. return
  35. link = os.readlink(path)
  36. if not os.path.isabs(link):
  37. return
  38. # find the common ancestor directory
  39. ancestor = path
  40. depth = 0
  41. while ancestor and not link.startswith(ancestor):
  42. ancestor = ancestor.rpartition('/')[0]
  43. depth += 1
  44. if not ancestor:
  45. print "make_relative_symlink() Error: unable to find the common ancestor of %s and its target" % path
  46. return
  47. base = link.partition(ancestor)[2].strip('/')
  48. while depth > 1:
  49. base = "../" + base
  50. depth -= 1
  51. os.remove(path)
  52. os.symlink(base, path)
  53. def format_display(path, metadata):
  54. """ Prepare a path for display to the user. """
  55. rel = relative(metadata.getVar("TOPDIR", True), path)
  56. if len(rel) > len(path):
  57. return path
  58. else:
  59. return rel
  60. def copytree(src, dst):
  61. # We could use something like shutil.copytree here but it turns out to
  62. # to be slow. It takes twice as long copying to an empty directory.
  63. # If dst already has contents performance can be 15 time slower
  64. # This way we also preserve hardlinks between files in the tree.
  65. bb.utils.mkdirhier(dst)
  66. cmd = 'tar -cf - -C %s -ps . | tar -xf - -C %s' % (src, dst)
  67. check_output(cmd, shell=True, stderr=subprocess.STDOUT)
  68. def remove(path, recurse=True):
  69. """Equivalent to rm -f or rm -rf"""
  70. for name in glob.glob(path):
  71. try:
  72. os.unlink(name)
  73. except OSError, exc:
  74. if recurse and exc.errno == errno.EISDIR:
  75. shutil.rmtree(name)
  76. elif exc.errno != errno.ENOENT:
  77. raise
  78. def symlink(source, destination, force=False):
  79. """Create a symbolic link"""
  80. try:
  81. if force:
  82. remove(destination)
  83. os.symlink(source, destination)
  84. except OSError, e:
  85. if e.errno != errno.EEXIST or os.readlink(destination) != source:
  86. raise
  87. class CalledProcessError(Exception):
  88. def __init__(self, retcode, cmd, output = None):
  89. self.retcode = retcode
  90. self.cmd = cmd
  91. self.output = output
  92. def __str__(self):
  93. return "Command '%s' returned non-zero exit status %d with output %s" % (self.cmd, self.retcode, self.output)
  94. # Not needed when we move to python 2.7
  95. def check_output(*popenargs, **kwargs):
  96. r"""Run command with arguments and return its output as a byte string.
  97. If the exit code was non-zero it raises a CalledProcessError. The
  98. CalledProcessError object will have the return code in the returncode
  99. attribute and output in the output attribute.
  100. The arguments are the same as for the Popen constructor. Example:
  101. >>> check_output(["ls", "-l", "/dev/null"])
  102. 'crw-rw-rw- 1 root root 1, 3 Oct 18 2007 /dev/null\n'
  103. The stdout argument is not allowed as it is used internally.
  104. To capture standard error in the result, use stderr=STDOUT.
  105. >>> check_output(["/bin/sh", "-c",
  106. ... "ls -l non_existent_file ; exit 0"],
  107. ... stderr=STDOUT)
  108. 'ls: non_existent_file: No such file or directory\n'
  109. """
  110. if 'stdout' in kwargs:
  111. raise ValueError('stdout argument not allowed, it will be overridden.')
  112. process = subprocess.Popen(stdout=subprocess.PIPE, *popenargs, **kwargs)
  113. output, unused_err = process.communicate()
  114. retcode = process.poll()
  115. if retcode:
  116. cmd = kwargs.get("args")
  117. if cmd is None:
  118. cmd = popenargs[0]
  119. raise CalledProcessError(retcode, cmd, output=output)
  120. return output
  121. def find(dir, **walkoptions):
  122. """ Given a directory, recurses into that directory,
  123. returning all files as absolute paths. """
  124. for root, dirs, files in os.walk(dir, **walkoptions):
  125. for file in files:
  126. yield os.path.join(root, file)