@@ -765,9 +765,13 @@ def count_files(self):
765765 """
766766 Determine number of files (sources + patches) required for this easyconfig.
767767 """
768- cnt = len (self ['sources' ]) + len (self ['patches' ])
769768
770- for ext in self ['exts_list' ]:
769+ # No need to resolve templates as we only need a count not the names
770+ with self .disable_templating ():
771+ cnt = len (self ['sources' ]) + len (self ['patches' ])
772+ exts = self ['exts_list' ]
773+
774+ for ext in exts :
771775 if isinstance (ext , tuple ) and len (ext ) >= 3 :
772776 ext_opts = ext [2 ]
773777 # check for 'sources' first, since that's also considered first by EasyBlock.collect_exts_file_info
@@ -1653,8 +1657,9 @@ def _finalize_dependencies(self):
16531657 filter_deps_specs = self .parse_filter_deps ()
16541658
16551659 for key in DEPENDENCY_PARAMETERS :
1656- # loop over a *copy* of dependency dicts (with resolved templates);
1657- deps = self [key ]
1660+ # loop over a *copy* of dependency dicts with resolved templates,
1661+ # although some templates may not resolve yet (e.g. those relying on dependencies like %(pyver)s)
1662+ deps = resolve_template (self .get_ref (key ), self .template_values , expect_resolved = False )
16581663
16591664 # to update the original dep dict, we need to get a reference with templating disabled...
16601665 deps_ref = self .get_ref (key )
@@ -1830,11 +1835,14 @@ def get(self, key, default=None, resolve=True):
18301835 # see also https://docs.python.org/2/reference/datamodel.html#object.__eq__
18311836 def __eq__ (self , ec ):
18321837 """Is this EasyConfig instance equivalent to the provided one?"""
1833- return self .asdict () == ec .asdict ()
1838+ # Compare raw values to check that templates used are the same
1839+ with self .disable_templating ():
1840+ with ec .disable_templating ():
1841+ return self .asdict () == ec .asdict ()
18341842
18351843 def __ne__ (self , ec ):
18361844 """Is this EasyConfig instance equivalent to the provided one?"""
1837- return self . asdict () != ec . asdict ()
1845+ return not self == ec
18381846
18391847 def __hash__ (self ):
18401848 """Return hash value for a hashable representation of this EasyConfig instance."""
@@ -1847,8 +1855,9 @@ def make_hashable(val):
18471855 return val
18481856
18491857 lst = []
1850- for (key , val ) in sorted (self .asdict ().items ()):
1851- lst .append ((key , make_hashable (val )))
1858+ with self .disable_templating ():
1859+ for (key , val ) in sorted (self .asdict ().items ()):
1860+ lst .append ((key , make_hashable (val )))
18521861
18531862 # a list is not hashable, but a tuple is
18541863 return hash (tuple (lst ))
@@ -1861,7 +1870,10 @@ def asdict(self):
18611870 for key , tup in self ._config .items ():
18621871 value = tup [0 ]
18631872 if self .enable_templating :
1864- value = self .resolve_template (value )
1873+ if not self .template_values :
1874+ self .generate_template_values ()
1875+ # Not all values can be resolved, e.g. %(installdir)s
1876+ value = resolve_template (value , self .template_values , expect_resolved = False )
18651877 res [key ] = value
18661878 return res
18671879
@@ -2009,10 +2021,11 @@ def get_module_path(name, generic=None, decode=True):
20092021 return '.' .join (modpath + [module_name ])
20102022
20112023
2012- def resolve_template (value , tmpl_dict ):
2024+ def resolve_template (value , tmpl_dict , expect_resolved = True ):
20132025 """Given a value, try to susbstitute the templated strings with actual values.
20142026 - value: some python object (supported are string, tuple/list, dict or some mix thereof)
20152027 - tmpl_dict: template dictionary
2028+ - expect_resolved: Expects that all templates get resolved
20162029 """
20172030 if isinstance (value , str ):
20182031 # simple escaping, making all '%foo', '%%foo', '%%%foo' post-templates values available,
@@ -2065,7 +2078,14 @@ def resolve_template(value, tmpl_dict):
20652078 _log .deprecated (f"Easyconfig template '{ old_tmpl } ' is deprecated, use '{ new_tmpl } ' instead" ,
20662079 ver )
20672080 except KeyError :
2068- _log .warning (f"Unable to resolve template value { value } with dict { tmpl_dict } " )
2081+ if expect_resolved :
2082+ msg = (f'Failed to resolve all templates in "{ value } " using template dictionary: { tmpl_dict } . '
2083+ 'This might cause failures or unexpected behavior, '
2084+ 'check for correct escaping if this is intended!' )
2085+ if build_option ('allow_unresolved_templates' ):
2086+ print_warning (msg )
2087+ else :
2088+ raise EasyBuildError (msg )
20692089 value = raw_value # Undo "%"-escaping
20702090
20712091 for key in tmpl_dict :
@@ -2086,11 +2106,12 @@ def resolve_template(value, tmpl_dict):
20862106 # self._config['x']['y'] = z
20872107 # it can not be intercepted with __setitem__ because the set is done at a deeper level
20882108 if isinstance (value , list ):
2089- value = [resolve_template (val , tmpl_dict ) for val in value ]
2109+ value = [resolve_template (val , tmpl_dict , expect_resolved ) for val in value ]
20902110 elif isinstance (value , tuple ):
2091- value = tuple (resolve_template (list (value ), tmpl_dict ))
2111+ value = tuple (resolve_template (list (value ), tmpl_dict , expect_resolved ))
20922112 elif isinstance (value , dict ):
2093- value = {resolve_template (k , tmpl_dict ): resolve_template (v , tmpl_dict ) for k , v in value .items ()}
2113+ value = {resolve_template (k , tmpl_dict , expect_resolved ): resolve_template (v , tmpl_dict , expect_resolved )
2114+ for k , v in value .items ()}
20942115
20952116 return value
20962117
0 commit comments