From b660a8c619dfa51c1797db0e22b16071fa54883b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrg=20Billeter?= Date: Tue, 26 Feb 2019 08:40:10 +0100 Subject: utils.py: safe_link(): Unlink only if target already exists Unlike shutil.copyfile(), os.link() will not modify the target file if it exists already. This improves staging performance by about 10% in a simple test. --- buildstream/utils.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/buildstream/utils.py b/buildstream/utils.py index a4e84097b..204afca0c 100644 --- a/buildstream/utils.py +++ b/buildstream/utils.py @@ -280,7 +280,7 @@ def safe_copy(src, dest, *, result=None): .format(src, dest, e)) from e -def safe_link(src, dest, *, result=None): +def safe_link(src, dest, *, result=None, _unlink=False): """Try to create a hardlink, but resort to copying in the case of cross device links. Args: @@ -292,19 +292,23 @@ def safe_link(src, dest, *, result=None): UtilError: In the case of unexpected system call failures """ - # First unlink the target if it exists - try: - os.unlink(dest) - except OSError as e: - if e.errno != errno.ENOENT: - raise UtilError("Failed to remove destination file '{}': {}" - .format(dest, e)) from e + if _unlink: + # First unlink the target if it exists + try: + os.unlink(dest) + except OSError as e: + if e.errno != errno.ENOENT: + raise UtilError("Failed to remove destination file '{}': {}" + .format(dest, e)) from e # If we can't link it due to cross-device hardlink, copy try: os.link(src, dest) except OSError as e: - if e.errno == errno.EXDEV: + if e.errno == errno.EEXIST and not _unlink: + # Target exists already, unlink and try again + safe_link(src, dest, result=result, _unlink=True) + elif e.errno == errno.EXDEV: safe_copy(src, dest) else: raise UtilError("Failed to link '{} -> {}': {}" -- cgit v1.2.1