diff --git a/easybuild/framework/easyconfig/default.py b/easybuild/framework/easyconfig/default.py index 1147878a43..58b08e7f3b 100644 --- a/easybuild/framework/easyconfig/default.py +++ b/easybuild/framework/easyconfig/default.py @@ -134,7 +134,8 @@ 'sanity_check_commands': [[], ("format: [(name, options)] e.g. [('gzip','-h')]. " "Using a non-tuple is equivalent to (name, '-h')"), BUILD], 'sanity_check_paths': [{}, ("List of files and directories to check " - "(format: {'files':, 'dirs':})"), BUILD], + "(format: {'files':, 'dirs':}). " + "Architecture specific list items are supported."), BUILD], 'skip': [False, "Skip existing software", BUILD], 'skip_mod_files_sanity_check': [False, "Skip the check for .mod files in a GCCcore level install", BUILD], 'skipsteps': [[], "Skip these steps", BUILD], @@ -169,8 +170,10 @@ # DEPENDENCIES easyconfig parameters 'allow_system_deps': [[], "Allow listed system dependencies (format: (, ))", DEPENDENCIES], - 'builddependencies': [[], "List of build dependencies", DEPENDENCIES], - 'dependencies': [[], "List of dependencies", DEPENDENCIES], + 'builddependencies': [[], ("List of build dependencies Architecture specific versions are supported " + "with 'False' to exclude the dependency"), DEPENDENCIES], + 'dependencies': [[], ("List of dependencies. Architecture specific versions are supported " + "with 'False' to exclude the dependency"), DEPENDENCIES], 'hiddendependencies': [[], "List of dependencies available as hidden modules", DEPENDENCIES], 'multi_deps': [{}, "Dict of lists of dependency versions over which to iterate", DEPENDENCIES], 'multi_deps_load_default': [True, "Load module for first version listed in multi_deps by default", DEPENDENCIES], diff --git a/easybuild/framework/easyconfig/templates.py b/easybuild/framework/easyconfig/templates.py index 8acf7a57ee..b87b35916a 100644 --- a/easybuild/framework/easyconfig/templates.py +++ b/easybuild/framework/easyconfig/templates.py @@ -440,13 +440,15 @@ def template_constant_dict(config, ignore=None, toolchain=None): if isinstance(dep_name, str) and dep_version: pref = name_to_prefix.get(dep_name.lower()) if pref: + # Resolve version if not already done dep_version = pick_dep_version(dep_version) - template_values['%sver' % pref] = dep_version - dep_version_parts = dep_version.split('.') - template_values['%smajver' % pref] = dep_version_parts[0] - if len(dep_version_parts) > 1: - template_values['%sminver' % pref] = dep_version_parts[1] - template_values['%sshortver' % pref] = '.'.join(dep_version_parts[:2]) + if dep_version: + template_values['%sver' % pref] = dep_version + dep_version_parts = dep_version.split('.') + template_values['%smajver' % pref] = dep_version_parts[0] + if len(dep_version_parts) > 1: + template_values['%sminver' % pref] = dep_version_parts[1] + template_values['%sshortver' % pref] = '.'.join(dep_version_parts[:2]) # step 2.1: CUDA templates in NVHPC if toolchain is not None and hasattr(toolchain, 'name') and toolchain.name in TEMPLATE_CUDA_VERSION_NVHPC: diff --git a/test/framework/easyconfig.py b/test/framework/easyconfig.py index 2c4742644b..285f920bbe 100644 --- a/test/framework/easyconfig.py +++ b/test/framework/easyconfig.py @@ -3687,18 +3687,22 @@ def test_template_constant_dict(self): my_arch = st.get_cpu_architecture() # add Java dep with version specified using a dict value - toy_ec_txt += '\n'.join([ - "dependencies += [", - " ('Python', '3.7.2')," - " ('Java', {", - " 'arch=%s': '1.8.0_221'," % my_arch, - " 'arch=fooarch': '1.8.0-foo',", - " })", - "]", - "builddependencies = [", - " ('CMake', '3.18.4'),", - "]", - ]) + toy_ec_txt += textwrap.dedent(""" + dependencies += [ + ('Python', '3.7.2'), + ('Java', { + 'arch=': '1.8.0_221', + 'arch=fooarch': '1.8.0-foo', + }), + ('Perl', { + 'arch=': False, + 'arch=fooarch': '1.42', + }), + ] + builddependencies = [ + ('CMake', '3.18.4'), + ] + """).replace('', my_arch) test_ec = os.path.join(self.test_prefix, 'test.eb') write_file(test_ec, toy_ec_txt) @@ -3733,39 +3737,28 @@ def test_template_constant_dict(self): } # proper EasyConfig instance - ec = EasyConfig(test_ec) - - # CMake should *not* be included, since it's a build-only dependency - dep_names = [x['name'] for x in ec['dependencies']] - self.assertFalse('CMake' in dep_names, "CMake should not be included in list of dependencies: %s" % dep_names) - res = template_constant_dict(ec) - dep_names = [x['name'] for x in ec['dependencies']] - self.assertFalse('CMake' in dep_names, "CMake should not be included in list of dependencies: %s" % dep_names) - - self.assertIn('arch', res) - arch = res.pop('arch') - self.assertTrue(arch_regex.match(arch), "'%s' matches with pattern '%s'" % (arch, arch_regex.pattern)) - - self.assertEqual(res, expected) - + full_ec = EasyConfig(test_ec) + expected_full = expected # only perform shallow/quick parse (as is done in list_software function) - ec = EasyConfigParser(filename=test_ec).get_config_dict() - - expected['module_name'] = None + shallow_ec = EasyConfigParser(filename=test_ec).get_config_dict() + expected_shallow = expected.copy() + expected_shallow['module_name'] = None for key in ('bitbucket_account', 'github_account', 'versionprefix'): - del expected[key] + del expected_shallow[key] - dep_names = [x[0] for x in ec['dependencies']] - self.assertFalse('CMake' in dep_names, "CMake should not be included in list of dependencies: %s" % dep_names) - res = template_constant_dict(ec) - dep_names = [x[0] for x in ec['dependencies']] - self.assertFalse('CMake' in dep_names, "CMake should not be included in list of dependencies: %s" % dep_names) + for name, ec, expected in (('Full', full_ec, expected_full), ('Shallow', shallow_ec, expected_shallow)): + with self.subTest(f'{name} easyconfig'): + # CMake should *not* be included, since it's a build-only dependency + dep_names = [x['name'] if isinstance(x, dict) else x[0] for x in ec['dependencies']] + self.assertNotIn('CMake', dep_names) - self.assertIn('arch', res) - arch = res.pop('arch') - self.assertTrue(arch_regex.match(arch), "'%s' matches with pattern '%s'" % (arch, arch_regex.pattern)) + res = template_constant_dict(ec) + self.assertIn('arch', res) + arch = res.pop('arch') + self.assertRegex(arch, arch_regex) - self.assertEqual(res, expected) + self.assertNotIn('perlver', res, "Perl should be filtered out") + self.assertEqual(res, expected) # also check result of template_constant_dict when dict representing extension is passed ext_dict = {