Skip to content

Commit e986ddf

Browse files
authored
Merge pull request #3142 from boegel/copy_ec
add support for --copy-ec
2 parents 17ddaa4 + 8592d75 commit e986ddf

File tree

5 files changed

+157
-8
lines changed

5 files changed

+157
-8
lines changed

easybuild/main.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@
5656
from easybuild.tools.config import find_last_log, get_repository, get_repositorypath, build_option
5757
from easybuild.tools.containers.common import containerize
5858
from easybuild.tools.docs import list_software
59-
from easybuild.tools.filetools import adjust_permissions, cleanup, read_file, write_file
59+
from easybuild.tools.filetools import adjust_permissions, cleanup, copy_file, copy_files, read_file, write_file
6060
from easybuild.tools.github import check_github, close_pr, new_branch_github, find_easybuild_easyconfig
6161
from easybuild.tools.github import install_github_token, list_prs, new_pr, new_pr_from_branch, merge_pr
6262
from easybuild.tools.github import sync_branch_with_develop, sync_pr_with_develop, update_branch, update_pr
@@ -291,6 +291,9 @@ def main(args=None, logfile=None, do_build=None, testing=False, modtool=None):
291291
eb_file = find_easybuild_easyconfig()
292292
orig_paths.append(eb_file)
293293

294+
# last path is target when --copy-ec is used, so remove that from the list
295+
target_path = orig_paths.pop() if options.copy_ec else None
296+
294297
categorized_paths = categorize_files_by_type(orig_paths)
295298

296299
# command line options that do not require any easyconfigs to be specified
@@ -302,9 +305,17 @@ def main(args=None, logfile=None, do_build=None, testing=False, modtool=None):
302305
# determine paths to easyconfigs
303306
determined_paths = det_easyconfig_paths(categorized_paths['easyconfigs'])
304307

305-
if options.fix_deprecated_easyconfigs or options.show_ec:
306-
if options.fix_deprecated_easyconfigs:
308+
if options.copy_ec or options.fix_deprecated_easyconfigs or options.show_ec:
309+
310+
if options.copy_ec:
311+
if len(determined_paths) == 1:
312+
copy_file(determined_paths[0], target_path)
313+
else:
314+
copy_files(determined_paths, target_path)
315+
316+
elif options.fix_deprecated_easyconfigs:
307317
fix_deprecated_easyconfigs(determined_paths)
318+
308319
elif options.show_ec:
309320
for path in determined_paths:
310321
print_msg("Contents of %s:" % path)

easybuild/tools/filetools.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1744,7 +1744,7 @@ def copy_file(path, target_path, force_in_dry_run=False):
17441744
17451745
:param path: the original filepath
17461746
:param target_path: path to copy the file to
1747-
:param force_in_dry_run: force running the command during dry run
1747+
:param force_in_dry_run: force copying of file during dry run
17481748
"""
17491749
if not force_in_dry_run and build_option('extended_dry_run'):
17501750
dry_run_msg("copied file %s to %s" % (path, target_path))
@@ -1767,6 +1767,28 @@ def copy_file(path, target_path, force_in_dry_run=False):
17671767
raise EasyBuildError("Failed to copy file %s to %s: %s", path, target_path, err)
17681768

17691769

1770+
def copy_files(paths, target_dir, force_in_dry_run=False):
1771+
"""
1772+
Copy list of files to specified target directory (which is created if it doesn't exist yet).
1773+
1774+
:param filepaths: list of files to copy
1775+
:param target_dir: target directory to copy files into
1776+
:param force_in_dry_run: force copying of files during dry run
1777+
"""
1778+
if not force_in_dry_run and build_option('extended_dry_run'):
1779+
dry_run_msg("copied files %s to %s" % (paths, target_dir))
1780+
else:
1781+
if os.path.exists(target_dir):
1782+
if os.path.isdir(target_dir):
1783+
_log.info("Copying easyconfigs into existing directory %s...", target_dir)
1784+
else:
1785+
raise EasyBuildError("%s exists but is not a directory", target_dir)
1786+
else:
1787+
mkdir(target_dir, parents=True)
1788+
for path in paths:
1789+
copy_file(path, target_dir)
1790+
1791+
17701792
def copy_dir(path, target_path, force_in_dry_run=False, **kwargs):
17711793
"""
17721794
Copy a directory from specified location to specified location

easybuild/tools/options.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,7 @@ def override_options(self):
342342
{'metavar': 'WHEN'}),
343343
'consider-archived-easyconfigs': ("Also consider archived easyconfigs", None, 'store_true', False),
344344
'containerize': ("Generate container recipe/image", None, 'store_true', False, 'C'),
345+
'copy-ec': ("Copy specified easyconfig(s) to specified location", None, 'store_true', False),
345346
'debug-lmod': ("Run Lmod modules tool commands in debug module", None, 'store_true', False),
346347
'default-opt-level': ("Specify default optimisation level", 'choice', 'store', DEFAULT_OPT_LEVEL,
347348
Compiler.COMPILER_OPT_FLAGS),

test/framework/filetools.py

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1324,7 +1324,7 @@ def test_apply_patch(self):
13241324
self.assertErrorRegex(EasyBuildError, "Couldn't apply patch file", ft.apply_patch, toy_patch, path)
13251325

13261326
def test_copy_file(self):
1327-
""" Test copy_file """
1327+
"""Test copy_file function."""
13281328
testdir = os.path.dirname(os.path.abspath(__file__))
13291329
to_copy = os.path.join(testdir, 'easyconfigs', 'test_ecs', 't', 'toy', 'toy-0.0.eb')
13301330
target_path = os.path.join(self.test_prefix, 'toy.eb')
@@ -1385,8 +1385,44 @@ def test_copy_file(self):
13851385
self.assertTrue(ft.read_file(to_copy) == ft.read_file(target_path))
13861386
self.assertEqual(txt, '')
13871387

1388+
def test_copy_files(self):
1389+
"""Test copy_files function."""
1390+
test_ecs = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'easyconfigs', 'test_ecs')
1391+
toy_ec = os.path.join(test_ecs, 't', 'toy', 'toy-0.0.eb')
1392+
toy_ec_txt = ft.read_file(toy_ec)
1393+
bzip2_ec = os.path.join(test_ecs, 'b', 'bzip2', 'bzip2-1.0.6-GCC-4.9.2.eb')
1394+
bzip2_ec_txt = ft.read_file(bzip2_ec)
1395+
1396+
# copying a single file to a non-existing directory
1397+
target_dir = os.path.join(self.test_prefix, 'target_dir1')
1398+
ft.copy_files([toy_ec], target_dir)
1399+
copied_toy_ec = os.path.join(target_dir, 'toy-0.0.eb')
1400+
self.assertTrue(os.path.exists(copied_toy_ec))
1401+
self.assertEqual(ft.read_file(copied_toy_ec), toy_ec_txt)
1402+
1403+
# copying a single file to an existing directory
1404+
ft.copy_files([bzip2_ec], target_dir)
1405+
copied_bzip2_ec = os.path.join(target_dir, 'bzip2-1.0.6-GCC-4.9.2.eb')
1406+
self.assertTrue(os.path.exists(copied_bzip2_ec))
1407+
self.assertEqual(ft.read_file(copied_bzip2_ec), bzip2_ec_txt)
1408+
1409+
# copying multiple files to a non-existing directory
1410+
target_dir = os.path.join(self.test_prefix, 'target_dir_multiple')
1411+
ft.copy_files([toy_ec, bzip2_ec], target_dir)
1412+
copied_toy_ec = os.path.join(target_dir, 'toy-0.0.eb')
1413+
self.assertTrue(os.path.exists(copied_toy_ec))
1414+
self.assertEqual(ft.read_file(copied_toy_ec), toy_ec_txt)
1415+
copied_bzip2_ec = os.path.join(target_dir, 'bzip2-1.0.6-GCC-4.9.2.eb')
1416+
self.assertTrue(os.path.exists(copied_bzip2_ec))
1417+
self.assertEqual(ft.read_file(copied_bzip2_ec), bzip2_ec_txt)
1418+
1419+
# copying files to an existing target that is not a directory results in an error
1420+
self.assertTrue(os.path.isfile(copied_toy_ec))
1421+
error_pattern = "/toy-0.0.eb exists but is not a directory"
1422+
self.assertErrorRegex(EasyBuildError, error_pattern, ft.copy_files, [bzip2_ec], copied_toy_ec)
1423+
13881424
def test_copy_dir(self):
1389-
"""Test copy_file"""
1425+
"""Test copy_dir function."""
13901426
testdir = os.path.dirname(os.path.abspath(__file__))
13911427
to_copy = os.path.join(testdir, 'easyconfigs', 'test_ecs', 'g', 'GCC')
13921428

test/framework/options.py

Lines changed: 81 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@
4949
from easybuild.tools.config import DEFAULT_MODULECLASSES
5050
from easybuild.tools.config import find_last_log, get_build_log_path, get_module_syntax, module_classes
5151
from easybuild.tools.environment import modify_env
52-
from easybuild.tools.filetools import copy_dir, copy_file, download_file, mkdir, read_file, remove_file
53-
from easybuild.tools.filetools import which, write_file
52+
from easybuild.tools.filetools import change_dir, copy_dir, copy_file, download_file, mkdir, read_file
53+
from easybuild.tools.filetools import remove_dir, remove_file, which, write_file
5454
from easybuild.tools.github import GITHUB_RAW, GITHUB_EB_MAIN, GITHUB_EASYCONFIGS_REPO
5555
from easybuild.tools.github import URL_SEPARATOR, fetch_github_token
5656
from easybuild.tools.modules import Lmod
@@ -828,6 +828,85 @@ def test_show_ec(self):
828828
regex = re.compile(pattern, re.M)
829829
self.assertTrue(regex.search(stdout), "Pattern '%s' found in: %s" % (regex.pattern, stdout))
830830

831+
def test_copy_ec(self):
832+
"""Test --copy-ec."""
833+
834+
topdir = os.path.dirname(os.path.abspath(__file__))
835+
test_easyconfigs_dir = os.path.join(topdir, 'easyconfigs', 'test_ecs')
836+
837+
toy_ec_txt = read_file(os.path.join(test_easyconfigs_dir, 't', 'toy', 'toy-0.0.eb'))
838+
bzip2_ec_txt = read_file(os.path.join(test_easyconfigs_dir, 'b', 'bzip2', 'bzip2-1.0.6-GCC-4.9.2.eb'))
839+
840+
# basic test: copying one easyconfig file to a non-existing absolute path
841+
test_ec = os.path.join(self.test_prefix, 'test.eb')
842+
args = ['--copy-ec', 'toy-0.0.eb', test_ec]
843+
self.eb_main(args)
844+
845+
self.assertTrue(os.path.exists(test_ec))
846+
self.assertEqual(toy_ec_txt, read_file(test_ec))
847+
848+
remove_file(test_ec)
849+
850+
# basic test: copying one easyconfig file to a non-existing relative path
851+
cwd = change_dir(self.test_prefix)
852+
target_fn = 'test.eb'
853+
self.assertFalse(os.path.exists(target_fn))
854+
855+
args = ['--copy-ec', 'toy-0.0.eb', target_fn]
856+
self.eb_main(args)
857+
858+
change_dir(cwd)
859+
860+
self.assertTrue(os.path.exists(test_ec))
861+
self.assertEqual(toy_ec_txt, read_file(test_ec))
862+
863+
# copying one easyconfig into an existing directory
864+
test_target_dir = os.path.join(self.test_prefix, 'test_target_dir')
865+
mkdir(test_target_dir)
866+
args = ['--copy-ec', 'toy-0.0.eb', test_target_dir]
867+
self.eb_main(args)
868+
869+
copied_toy_ec = os.path.join(test_target_dir, 'toy-0.0.eb')
870+
self.assertTrue(os.path.exists(copied_toy_ec))
871+
self.assertEqual(toy_ec_txt, read_file(copied_toy_ec))
872+
873+
remove_dir(test_target_dir)
874+
875+
def check_copied_files():
876+
"""Helper function to check result of copying multiple easyconfigs."""
877+
self.assertTrue(os.path.exists(test_target_dir))
878+
self.assertEqual(sorted(os.listdir(test_target_dir)), ['bzip2-1.0.6-GCC-4.9.2.eb', 'toy-0.0.eb'])
879+
copied_toy_ec = os.path.join(test_target_dir, 'toy-0.0.eb')
880+
self.assertTrue(os.path.exists(copied_toy_ec))
881+
self.assertEqual(toy_ec_txt, read_file(copied_toy_ec))
882+
copied_bzip2_ec = os.path.join(test_target_dir, 'bzip2-1.0.6-GCC-4.9.2.eb')
883+
self.assertTrue(os.path.exists(copied_bzip2_ec))
884+
self.assertEqual(bzip2_ec_txt, read_file(copied_bzip2_ec))
885+
886+
# copying multiple easyconfig files to a non-existing target directory (which is created automatically)
887+
args = ['--copy-ec', 'toy-0.0.eb', 'bzip2-1.0.6-GCC-4.9.2.eb', test_target_dir]
888+
self.eb_main(args)
889+
890+
check_copied_files()
891+
892+
remove_dir(test_target_dir)
893+
894+
# same but with relative path for target dir
895+
change_dir(self.test_prefix)
896+
args[-1] = os.path.basename(test_target_dir)
897+
self.assertFalse(os.path.exists(args[-1]))
898+
899+
self.eb_main(args)
900+
901+
check_copied_files()
902+
903+
# copying multiple easyconfig to an existing target file resuts in an error
904+
target = os.path.join(self.test_prefix, 'test.eb')
905+
self.assertTrue(os.path.isfile(target))
906+
args = ['--copy-ec', 'toy-0.0.eb', 'bzip2-1.0.6-GCC-4.9.2.eb', target]
907+
error_pattern = ".*/test.eb exists but is not a directory"
908+
self.assertErrorRegex(EasyBuildError, error_pattern, self.eb_main, args, raise_error=True)
909+
831910
def test_dry_run(self):
832911
"""Test dry run (long format)."""
833912
fd, dummylogfn = tempfile.mkstemp(prefix='easybuild-dummy', suffix='.log')

0 commit comments

Comments
 (0)