diff --git a/easybuild/tools/filetools.py b/easybuild/tools/filetools.py index 0e4afabe1a..723ac5d6e1 100644 --- a/easybuild/tools/filetools.py +++ b/easybuild/tools/filetools.py @@ -2271,7 +2271,11 @@ def copy_file(path, target_path, force_in_dry_run=False): :param force_in_dry_run: force copying of file during dry run """ if not force_in_dry_run and build_option('extended_dry_run'): + # If in dry run mode, do not copy any files, just lie about it dry_run_msg("copied file %s to %s" % (path, target_path)) + elif not os.path.exists(path) and not os.path.islink(path): + # NOTE: 'exists' will return False if 'path' is a broken symlink + raise EasyBuildError("Could not copy '%s' it does not exist!", path) else: try: target_exists = os.path.exists(target_path) @@ -2285,13 +2289,14 @@ def copy_file(path, target_path, force_in_dry_run=False): _log.info("Copied contents of file %s to %s", path, target_path) else: mkdir(os.path.dirname(target_path), parents=True) - if os.path.exists(path): - shutil.copy2(path, target_path) - elif os.path.islink(path): + if os.path.islink(path): # special care for copying broken symlinks link_target = os.readlink(path) symlink(link_target, target_path) - _log.info("%s copied to %s", path, target_path) + _log.info("created symlink from %s to %s", path, target_path) + else: + shutil.copy2(path, target_path) + _log.info("%s copied to %s", path, target_path) except (IOError, OSError, shutil.Error) as err: raise EasyBuildError("Failed to copy file %s to %s: %s", path, target_path, err) diff --git a/test/framework/filetools.py b/test/framework/filetools.py index 722710430f..ee9eb2ab48 100644 --- a/test/framework/filetools.py +++ b/test/framework/filetools.py @@ -45,7 +45,7 @@ from easybuild.tools import run import easybuild.tools.filetools as ft from easybuild.tools.build_log import EasyBuildError -from easybuild.tools.config import IGNORE, ERROR +from easybuild.tools.config import IGNORE, ERROR, update_build_option from easybuild.tools.multidiff import multidiff from easybuild.tools.py2vs3 import std_urllib @@ -1663,6 +1663,20 @@ def test_copy_file(self): self.assertTrue(ft.read_file(to_copy) == ft.read_file(target_path)) self.assertEqual(txt, '') + # Test that a non-existing file raises an exception + update_build_option('extended_dry_run', False) + src, target = os.path.join(self.test_prefix, 'this_file_does_not_exist'), os.path.join(self.test_prefix, 'toy') + self.assertErrorRegex(EasyBuildError, "Could not copy *", ft.copy_file, src, target) + # Test that copying a non-existing file in 'dry_run' mode does noting + update_build_option('extended_dry_run', True) + self.mock_stdout(True) + ft.copy_file(src, target, force_in_dry_run=False) + txt = self.get_stdout() + self.mock_stdout(False) + self.assertTrue(re.search("^copied file %s to %s" % (src, target), txt)) + # However, if we add 'force_in_dry_run=True' it should throw an exception + self.assertErrorRegex(EasyBuildError, "Could not copy *", ft.copy_file, src, target, force_in_dry_run=True) + def test_copy_files(self): """Test copy_files function.""" test_ecs = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'easyconfigs', 'test_ecs')