Skip to content
29 changes: 20 additions & 9 deletions easybuild/tools/systemtools.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@
POWER = 'POWER'
X86_64 = 'x86_64'

ARCH_KEY_PREFIX = 'arch='

# Vendor constants
AMD = 'AMD'
APM = 'Applied Micro'
Expand Down Expand Up @@ -921,20 +923,29 @@ def pick_dep_version(dep_version):
result = None

elif isinstance(dep_version, dict):
# figure out matches based on dict keys (after splitting on '=')
my_arch_key = 'arch=%s' % get_cpu_architecture()
arch_keys = [x for x in dep_version.keys() if x.startswith('arch=')]
arch_keys = [x for x in dep_version.keys() if x.startswith(ARCH_KEY_PREFIX)]
other_keys = [x for x in dep_version.keys() if x not in arch_keys]
if other_keys:
raise EasyBuildError("Unexpected keys in version: %s. Only 'arch=' keys are supported", other_keys)
other_keys = ','.join(sorted(other_keys))
raise EasyBuildError("Unexpected keys in version: %s (only 'arch=' keys are supported)", other_keys)
if arch_keys:
if my_arch_key in dep_version:
result = dep_version[my_arch_key]
_log.info("Version selected from %s using key %s: %s", dep_version, my_arch_key, result)
host_arch_key = ARCH_KEY_PREFIX + get_cpu_architecture()
star_arch_key = ARCH_KEY_PREFIX + '*'
# check for specific 'arch=' key first
if host_arch_key in dep_version:
result = dep_version[host_arch_key]
_log.info("Version selected from %s using key %s: %s", dep_version, host_arch_key, result)
# fall back to 'arch=*'
elif star_arch_key in dep_version:
result = dep_version[star_arch_key]
_log.info("Version selected for %s using fallback key %s: %s", dep_version, star_arch_key, result)
else:
raise EasyBuildError("No matches for version in %s (looking for %s)", dep_version, my_arch_key)
raise EasyBuildError("No matches for version in %s (looking for %s)", dep_version, host_arch_key)
else:
raise EasyBuildError("Found empty dict as version!")

else:
raise EasyBuildError("Unknown value type for version: %s", dep_version)
typ = type(dep_version)
raise EasyBuildError("Unknown value type for version: %s (%s), should be string value", typ, dep_version)

return result
67 changes: 66 additions & 1 deletion test/framework/easyconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@
from easybuild.tools.options import parse_external_modules_metadata
from easybuild.tools.py2vs3 import OrderedDict, reload
from easybuild.tools.robot import resolve_dependencies
from easybuild.tools.systemtools import get_shared_lib_ext
from easybuild.tools.systemtools import AARCH64, POWER, X86_64, get_cpu_architecture, get_shared_lib_ext
from easybuild.tools.toolchain.utilities import search_toolchain
from easybuild.tools.utilities import quote_str, quote_py_str
from test.framework.utilities import find_full_path
Expand Down Expand Up @@ -104,6 +104,7 @@ class EasyConfigTest(EnhancedTestCase):
def setUp(self):
"""Set up everything for running a unit test."""
super(EasyConfigTest, self).setUp()
self.orig_get_cpu_architecture = st.get_cpu_architecture

self.cwd = os.getcwd()
self.all_stops = [x[0] for x in EasyBlock.get_steps()]
Expand All @@ -122,6 +123,7 @@ def prep(self):

def tearDown(self):
""" make sure to remove the temporary file """
st.get_cpu_architecture = self.orig_get_cpu_architecture
super(EasyConfigTest, self).tearDown()
if os.path.exists(self.eb_file):
os.remove(self.eb_file)
Expand Down Expand Up @@ -305,6 +307,69 @@ def test_dependency(self):
self.assertErrorRegex(EasyBuildError, err_msg, eb._parse_dependency, (EXTERNAL_MODULE_MARKER,))
self.assertErrorRegex(EasyBuildError, err_msg, eb._parse_dependency, ('foo', '1.2.3', EXTERNAL_MODULE_MARKER))

def test_false_dep_version(self):
"""
Test use False as dependency version via dict using 'arch=' keys,
which should result in filtering the dependency.
"""
# silence warnings about missing easyconfigs for dependencies, we don't care
init_config(build_options={'silent': True})

arch = get_cpu_architecture()

self.contents = '\n'.join([
'easyblock = "ConfigureMake"',
'name = "pi"',
'version = "3.14"',
'versionsuffix = "-test"',
'homepage = "http://example.com"',
'description = "test easyconfig"',
'toolchain = {"name":"GCC", "version": "4.6.3"}',
'dependencies = ['
' ("first", "1.0"),',
' ("second", {"arch=%s": False}),' % arch,
']',
'builddependencies = [',
' ("first_build", {"arch=%s": False}),' % arch,
' ("second_build", "2.0"),',
']',
])
self.prep()
eb = EasyConfig(self.eb_file)
deps = eb.dependencies()
self.assertEqual(len(deps), 2)
self.assertEqual(deps[0]['name'], 'second_build')
self.assertEqual(deps[1]['name'], 'first')

# more realistic example: only filter dep for POWER
self.contents = '\n'.join([
'easyblock = "ConfigureMake"',
'name = "pi"',
'version = "3.14"',
'versionsuffix = "-test"',
'homepage = "http://example.com"',
'description = "test easyconfig"',
'toolchain = {"name":"GCC", "version": "4.6.3"}',
'dependencies = ['
' ("not_on_power", {"arch=*": "1.2.3", "arch=POWER": False}),',
']',
])
self.prep()

# only non-POWER arch, dependency is retained
for arch in (AARCH64, X86_64):
st.get_cpu_architecture = lambda: arch
eb = EasyConfig(self.eb_file)
deps = eb.dependencies()
self.assertEqual(len(deps), 1)
self.assertEqual(deps[0]['name'], 'not_on_power')

# only power, dependency gets filtered
st.get_cpu_architecture = lambda: POWER
eb = EasyConfig(self.eb_file)
deps = eb.dependencies()
self.assertEqual(deps, [])

def test_extra_options(self):
""" extra_options should allow other variables to be stored """
init_config(build_options={'silent': True})
Expand Down
18 changes: 18 additions & 0 deletions test/framework/systemtools.py
Original file line number Diff line number Diff line change
Expand Up @@ -916,6 +916,24 @@ def test_pick_dep_version(self):
error_pattern = "Unknown value type for version"
self.assertErrorRegex(EasyBuildError, error_pattern, pick_dep_version, ('1.2.3', '4.5.6'))

# check support for using 'arch=*' as fallback key
dep_ver_dict = {
'arch=*': '1.2.3',
'arch=foo': '1.2.3-foo',
'arch=POWER': '1.2.3-ppc64le',
}
self.assertEqual(pick_dep_version(dep_ver_dict), '1.2.3-ppc64le')

del dep_ver_dict['arch=POWER']
self.assertEqual(pick_dep_version(dep_ver_dict), '1.2.3')

# check how faulty input is handled
self.assertErrorRegex(EasyBuildError, "Found empty dict as version!", pick_dep_version, {})
error_pattern = r"Unexpected keys in version: bar,foo \(only 'arch=' keys are supported\)"
self.assertErrorRegex(EasyBuildError, error_pattern, pick_dep_version, {'foo': '1.2', 'bar': '2.3'})
error_pattern = r"Unknown value type for version: .* \(1.23\), should be string value"
self.assertErrorRegex(EasyBuildError, error_pattern, pick_dep_version, 1.23)

def test_check_os_dependency(self):
"""Test check_os_dependency."""

Expand Down