Skip to content

Commit 245151a

Browse files
authored
Merge pull request #3976 from ItIsI-Orient/20220308161919_new_pr_HyjCenCVwv
Added new parameter key to specify some download or installation steps for user in case of complicated way of obtaining needed files
2 parents ed04e07 + 16f55f8 commit 245151a

File tree

3 files changed

+124
-6
lines changed

3 files changed

+124
-6
lines changed

easybuild/framework/easyblock.py

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,7 @@ def get_checksum_for(self, checksums, filename=None, index=None):
364364
else:
365365
raise EasyBuildError("Invalid type for checksums (%s), should be list, tuple or None.", type(checksums))
366366

367-
def fetch_source(self, source, checksum=None, extension=False):
367+
def fetch_source(self, source, checksum=None, extension=False, download_instructions=None):
368368
"""
369369
Get a specific source (tarball, iso, url)
370370
Will be tested for existence or can be located
@@ -400,7 +400,8 @@ def fetch_source(self, source, checksum=None, extension=False):
400400
# check if the sources can be located
401401
force_download = build_option('force_download') in [FORCE_DOWNLOAD_ALL, FORCE_DOWNLOAD_SOURCES]
402402
path = self.obtain_file(filename, extension=extension, download_filename=download_filename,
403-
force_download=force_download, urls=source_urls, git_config=git_config)
403+
force_download=force_download, urls=source_urls, git_config=git_config,
404+
download_instructions=download_instructions)
404405
if path is None:
405406
raise EasyBuildError('No file found for source %s', filename)
406407

@@ -586,7 +587,8 @@ def collect_exts_file_info(self, fetch_files=True, verify_checksums=True):
586587
source['source_urls'] = source_urls
587588

588589
if fetch_files:
589-
src = self.fetch_source(source, checksums, extension=True)
590+
src = self.fetch_source(source, checksums, extension=True,
591+
download_instructions=ext_options.get('download_instructions'))
590592
ext_src.update({
591593
# keep track of custom extract command (if any)
592594
'extract_cmd': src['cmd'],
@@ -682,7 +684,7 @@ def collect_exts_file_info(self, fetch_files=True, verify_checksums=True):
682684
return exts_sources
683685

684686
def obtain_file(self, filename, extension=False, urls=None, download_filename=None, force_download=False,
685-
git_config=None):
687+
git_config=None, download_instructions=None):
686688
"""
687689
Locate the file with the given name
688690
- searches in different subdirectories of source path
@@ -869,8 +871,19 @@ def obtain_file(self, filename, extension=False, urls=None, download_filename=No
869871
self.dry_run_msg(" * %s (MISSING)", filename)
870872
return filename
871873
else:
872-
raise EasyBuildError("Couldn't find file %s anywhere, and downloading it didn't work either... "
873-
"Paths attempted (in order): %s ", filename, ', '.join(failedpaths))
874+
error_msg = "Couldn't find file %s anywhere, "
875+
if download_instructions is None:
876+
download_instructions = self.cfg['download_instructions']
877+
if download_instructions is not None and download_instructions != "":
878+
msg = "\nDownload instructions:\n\n" + download_instructions + '\n'
879+
print_msg(msg, prefix=False, stderr=True)
880+
error_msg += "please follow the download instructions above, and make the file available "
881+
error_msg += "in the active source path (%s)" % ':'.join(source_paths())
882+
else:
883+
error_msg += "and downloading it didn't work either... "
884+
error_msg += "Paths attempted (in order): %s " % ', '.join(failedpaths)
885+
886+
raise EasyBuildError(error_msg, filename)
874887

875888
#
876889
# GETTER/SETTER UTILITY FUNCTIONS

easybuild/framework/easyconfig/default.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@
9090
'checksums': [[], "Checksums for sources and patches", BUILD],
9191
'configopts': ['', 'Extra options passed to configure (default already has --prefix)', BUILD],
9292
'cuda_compute_capabilities': [[], "List of CUDA compute capabilities to build with (if supported)", BUILD],
93+
'download_instructions': ['', "Specify steps to aquire necessary file, if obtaining it is difficult", BUILD],
9394
'easyblock': [None, "EasyBlock to use for building; if set to None, an easyblock is selected "
9495
"based on the software name", BUILD],
9596
'easybuild_version': [None, "EasyBuild-version this spec-file was written for", BUILD],

test/framework/easyblock.py

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1480,6 +1480,110 @@ def test_fetch_sources(self):
14801480
error_pattern = "Found one or more unexpected keys in 'sources' specification: {'nosuchkey': 'foobar'}"
14811481
self.assertErrorRegex(EasyBuildError, error_pattern, eb.fetch_sources, sources, checksums=[])
14821482

1483+
def test_download_instructions(self):
1484+
"""Test use of download_instructions easyconfig parameter."""
1485+
orig_test_ec = '\n'.join([
1486+
"easyblock = 'ConfigureMake'",
1487+
"name = 'software_with_missing_sources'",
1488+
"version = '0.0'",
1489+
"homepage = 'https://example.com'",
1490+
"description = 'test'",
1491+
"toolchain = SYSTEM",
1492+
"sources = [SOURCE_TAR_GZ]",
1493+
"exts_list = [",
1494+
" ('ext_with_missing_sources', '0.0', {",
1495+
" 'sources': [SOURCE_TAR_GZ],",
1496+
" }),",
1497+
"]",
1498+
])
1499+
self.contents = orig_test_ec
1500+
self.writeEC()
1501+
eb = EasyBlock(EasyConfig(self.eb_file))
1502+
1503+
common_error_pattern = "^Couldn't find file software_with_missing_sources-0.0.tar.gz anywhere"
1504+
error_pattern = common_error_pattern + ", and downloading it didn't work either"
1505+
self.assertErrorRegex(EasyBuildError, error_pattern, eb.fetch_step)
1506+
1507+
download_instructions = "download_instructions = 'Manual download from example.com required'"
1508+
sources = "sources = [SOURCE_TAR_GZ]"
1509+
self.contents = self.contents.replace(sources, download_instructions + '\n' + sources)
1510+
self.writeEC()
1511+
eb = EasyBlock(EasyConfig(self.eb_file))
1512+
1513+
error_pattern = common_error_pattern + ", please follow the download instructions above"
1514+
self.mock_stderr(True)
1515+
self.mock_stdout(True)
1516+
self.assertErrorRegex(EasyBuildError, error_pattern, eb.fetch_step)
1517+
stderr = self.get_stderr().strip()
1518+
stdout = self.get_stdout().strip()
1519+
self.mock_stderr(False)
1520+
self.mock_stdout(False)
1521+
self.assertEqual(stderr, "Download instructions:\n\nManual download from example.com required")
1522+
self.assertEqual(stdout, '')
1523+
1524+
# create dummy source file
1525+
write_file(os.path.join(os.path.dirname(self.eb_file), 'software_with_missing_sources-0.0.tar.gz'), '')
1526+
1527+
# now downloading of sources for extension should fail
1528+
# top-level download instructions are printed (because there's nothing else)
1529+
error_pattern = "^Couldn't find file ext_with_missing_sources-0.0.tar.gz anywhere"
1530+
self.mock_stderr(True)
1531+
self.mock_stdout(True)
1532+
self.assertErrorRegex(EasyBuildError, error_pattern, eb.fetch_step)
1533+
stderr = self.get_stderr().strip()
1534+
stdout = self.get_stdout().strip()
1535+
self.mock_stderr(False)
1536+
self.mock_stdout(False)
1537+
self.assertEqual(stderr, "Download instructions:\n\nManual download from example.com required")
1538+
self.assertEqual(stdout, '')
1539+
1540+
# wipe top-level download instructions, try again
1541+
self.contents = self.contents.replace(download_instructions, '')
1542+
self.writeEC()
1543+
eb = EasyBlock(EasyConfig(self.eb_file))
1544+
1545+
# no download instructions printed anymore now
1546+
self.mock_stderr(True)
1547+
self.mock_stdout(True)
1548+
self.assertErrorRegex(EasyBuildError, error_pattern, eb.fetch_step)
1549+
stderr = self.get_stderr().strip()
1550+
stdout = self.get_stdout().strip()
1551+
self.mock_stderr(False)
1552+
self.mock_stdout(False)
1553+
self.assertEqual(stdout, '')
1554+
1555+
# inject download instructions for extension
1556+
download_instructions = ' ' * 8 + "'download_instructions': "
1557+
download_instructions += "'Extension sources must be downloaded via example.com',"
1558+
sources = "'sources': [SOURCE_TAR_GZ],"
1559+
self.contents = self.contents.replace(sources, sources + '\n' + download_instructions)
1560+
self.writeEC()
1561+
eb = EasyBlock(EasyConfig(self.eb_file))
1562+
1563+
self.mock_stderr(True)
1564+
self.mock_stdout(True)
1565+
self.assertErrorRegex(EasyBuildError, error_pattern, eb.fetch_step)
1566+
stderr = self.get_stderr().strip()
1567+
stdout = self.get_stdout().strip()
1568+
self.mock_stderr(False)
1569+
self.mock_stdout(False)
1570+
self.assertEqual(stderr, "Download instructions:\n\nExtension sources must be downloaded via example.com")
1571+
self.assertEqual(stdout, '')
1572+
1573+
# create dummy source file for extension
1574+
write_file(os.path.join(os.path.dirname(self.eb_file), 'ext_with_missing_sources-0.0.tar.gz'), '')
1575+
1576+
# no more errors, all source files found (so no download instructions printed either)
1577+
self.mock_stderr(True)
1578+
self.mock_stdout(True)
1579+
eb.fetch_step()
1580+
stderr = self.get_stderr().strip()
1581+
stdout = self.get_stdout().strip()
1582+
self.mock_stderr(False)
1583+
self.mock_stdout(False)
1584+
self.assertEqual(stderr, '')
1585+
self.assertEqual(stdout, '')
1586+
14831587
def test_fetch_patches(self):
14841588
"""Test fetch_patches method."""
14851589
testdir = os.path.abspath(os.path.dirname(__file__))

0 commit comments

Comments
 (0)