Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions easybuild/framework/easyconfig/default.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,8 @@

# OTHER easyconfig parameters
'buildstats': [None, "A list of dicts with build statistics", OTHER],
'deprecated': [False, "String specifying reason why this easyconfig file is deprecated "
"and will be archived in the next major release of EasyBuild", OTHER],
}


Expand Down
24 changes: 24 additions & 0 deletions easybuild/framework/easyconfig/easyconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
from easybuild.tools.toolchain.toolchain import TOOLCHAIN_CAPABILITIES, TOOLCHAIN_CAPABILITY_CUDA
from easybuild.tools.toolchain.utilities import get_toolchain, search_toolchain
from easybuild.tools.utilities import quote_py_str, remove_unwanted_chars
from easybuild.tools.version import VERSION
from easybuild.toolchains.compiler.cuda import Cuda

_log = fancylogger.getLogger('easyconfig.easyconfig', fname=False)
Expand Down Expand Up @@ -384,6 +385,9 @@ def __init__(self, path, extra_options=None, build_specs=None, validate=True, hi
self.build_specs = build_specs
self.parse()

# check whether this easyconfig file is deprecated, and act accordingly if so
self.check_deprecated(self.path)

# perform validations
self.validation = build_option('validate') and validate
if self.validation:
Expand Down Expand Up @@ -534,6 +538,26 @@ def parse(self):
# indicate that this is a parsed easyconfig
self._config['parsed'] = [True, "This is a parsed easyconfig", "HIDDEN"]

def check_deprecated(self, path):
"""Check whether this easyconfig file is deprecated."""

depr_msgs = []

deprecated = self['deprecated']
if deprecated:
if isinstance(deprecated, basestring):
depr_msgs.append("easyconfig file '%s' is marked as deprecated:\n%s\n" % (path, deprecated))
else:
raise EasyBuildError("Wrong type for value of 'deprecated' easyconfig parameter: %s", type(deprecated))

if self.toolchain.is_deprecated():
depr_msgs.append("toolchain '%(name)s/%(version)s' is marked as deprecated" % self['toolchain'])

if depr_msgs:
depr_maj_ver = int(str(VERSION).split('.')[0]) + 1
more_info_depr_ec = "(see also http://easybuild.readthedocs.org/en/latest/Deprecated-easyconfigs.html)"
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I should probably change this to target /develop in the docs while this is still in develop...

Cfr. easybuilders/easybuild#468

Thoughts @ocaisa?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that is so important, I imagine that you will only start inserting this deprecated string in shipped easyconfigs with a particular release

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto with deprecating toolchains, this will only be relevant when they are marked as deprecated in a release. Those pulling down develop are highly unlikely to be working with deprecated toolchains/easyconfigs.

self.log.deprecated(', '.join(depr_msgs), '%s.0' % depr_maj_ver, more_info=more_info_depr_ec)

def validate(self, check_osdeps=True):
"""
Validate this easyonfig
Expand Down
10 changes: 7 additions & 3 deletions easybuild/tools/build_log.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,28 +121,32 @@ def experimental(self, msg, *args, **kwargs):
msg = common_msg + " (use --experimental option to enable): " + msg
raise EasyBuildError(msg, *args)

def deprecated(self, msg, ver, max_ver=None, *args, **kwargs):
def deprecated(self, msg, ver, max_ver=None, more_info=None, *args, **kwargs):
"""
Print deprecation warning or raise an exception, depending on specified version(s)

:param: msg: deprecation message
:param ver: if max_ver is None: threshold for EasyBuild version to determine warning vs exception
else: version to check against max_ver to determine warning vs exception
:param max_ver: version threshold for warning vs exception (compared to 'ver')
:param more_info: additional message with instructions where to get more information
"""
# provide log_callback function that both logs a warning and prints to stderr
def log_callback_warning_and_print(msg):
"""Log warning message, and also print it to stderr."""
self.warning(msg)
sys.stderr.write(msg + '\n')
sys.stderr.write('\nWARNING: ' + msg + '\n\n')

kwargs['log_callback'] = log_callback_warning_and_print

# always raise an EasyBuildError, nothing else
kwargs['exception'] = EasyBuildError

if max_ver is None:
msg += "; see %s for more information" % DEPRECATED_DOC_URL
if more_info:
msg += more_info
else:
msg += "; see %s for more information" % DEPRECATED_DOC_URL
fancylogger.FancyLogger.deprecated(self, msg, str(CURRENT_VERSION), ver, *args, **kwargs)
else:
fancylogger.FancyLogger.deprecated(self, msg, ver, max_ver, *args, **kwargs)
Expand Down
4 changes: 4 additions & 0 deletions easybuild/tools/toolchain/toolchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -670,6 +670,10 @@ def compilers(self):

return (c_comps, fortran_comps)

def is_deprecated(self):
"""Return whether or not this toolchain is deprecated."""
return False

def prepare(self, onlymod=None, silent=False, loadmod=True, rpath_filter_dirs=None, rpath_include_dirs=None):
"""
Prepare a set of environment parameters based on name/version of toolchain
Expand Down
29 changes: 22 additions & 7 deletions test/framework/build_log.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,8 @@
import re
import sys
import tempfile
from distutils.version import LooseVersion
from datetime import datetime, timedelta
from test.framework.utilities import EnhancedTestCase, TestLoaderFiltered, init_config
from test.framework.utilities import EnhancedTestCase, TestLoaderFiltered
from unittest import TextTestRunner
from vsc.utils.fancylogger import getLogger, getRootLoggerName, logToFile, setLogFormat

Expand Down Expand Up @@ -115,11 +114,11 @@ def test_easybuildlog(self):
self.mock_stderr(False)

more_info = "see http://easybuild.readthedocs.org/en/latest/Deprecated-functionality.html for more information"
expected_stderr = '\n'.join([
"Deprecated functionality, will no longer work in v10000001: anotherwarning; " + more_info,
"Deprecated functionality, will no longer work in v2.0: onemorewarning",
"Deprecated functionality, will no longer work in v2.0: lastwarning",
]) + '\n'
expected_stderr = '\n\n'.join([
"\nWARNING: Deprecated functionality, will no longer work in v10000001: anotherwarning; " + more_info,
"\nWARNING: Deprecated functionality, will no longer work in v2.0: onemorewarning",
"\nWARNING: Deprecated functionality, will no longer work in v2.0: lastwarning",
]) + '\n\n'
self.assertEqual(stderr, expected_stderr)

try:
Expand Down Expand Up @@ -172,6 +171,21 @@ def test_easybuildlog(self):
logtxt_regex = re.compile(r'^%s' % expected_logtxt, re.M)
self.assertTrue(logtxt_regex.search(logtxt), "Pattern '%s' found in %s" % (logtxt_regex.pattern, logtxt))

write_file(tmplog, '')
logToFile(tmplog, enable=True)

# also test use of 'more_info' named argument for log.deprecated
self.mock_stderr(True)
log.deprecated("\nthis is just a test\n", newer_ver, more_info="(see URLGOESHERE for more information)")
self.mock_stderr(False)
logtxt = read_file(tmplog)
expected_logtxt = '\n'.join([
"[WARNING] :: Deprecated functionality, will no longer work in v10000001: ",
"this is just a test",
"(see URLGOESHERE for more information)",
])
self.assertTrue(logtxt.strip().endswith(expected_logtxt))

def test_log_levels(self):
"""Test whether log levels are respected"""
fd, tmplog = tempfile.mkstemp()
Expand Down Expand Up @@ -274,5 +288,6 @@ def suite():
""" returns all the testcases in this module """
return TestLoaderFiltered().loadTestsFromTestCase(BuildLogTest, sys.argv[1:])


if __name__ == '__main__':
TextTestRunner(verbosity=1).run(suite())
10 changes: 10 additions & 0 deletions test/framework/easyconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -2321,6 +2321,16 @@ def test_check_sha256_checksums(self):
# multiple checksums listed for source tarball, while exactly one (SHA256) checksum is expected
self.assertTrue(res[1].startswith("Non-SHA256 checksum found for toy-0.0.tar.gz: "))

def test_deprecated(self):
"""Test use of 'deprecated' easyconfig parameter."""
topdir = os.path.dirname(os.path.abspath(__file__))
toy_ec_txt = read_file(os.path.join(topdir, 'easyconfigs', 'test_ecs', 't', 'toy', 'toy-0.0.eb'))
test_ec = os.path.join(self.test_prefix, 'test.eb')
write_file(test_ec, toy_ec_txt + "\ndeprecated = 'this is just a test'")

error_pattern = r"easyconfig file '.*/test.eb' is marked as deprecated:\nthis is just a test\n\(see also"
self.assertErrorRegex(EasyBuildError, error_pattern, EasyConfig, test_ec)


def suite():
""" returns all the testcases in this module """
Expand Down
2 changes: 1 addition & 1 deletion test/framework/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -1343,7 +1343,7 @@ def test_deprecated(self):
except easybuild.tools.build_log.EasyBuildError, err:
self.assertTrue(False, 'Deprecated logging should work')

stderr_regex = re.compile("^Deprecated functionality, will no longer work in")
stderr_regex = re.compile("^\nWARNING: Deprecated functionality, will no longer work in")
self.assertTrue(stderr_regex.search(stderr), "Pattern '%s' found in: %s" % (stderr_regex.pattern, stderr))

# force it to current version, which should result in deprecation
Expand Down