@@ -64,6 +64,12 @@ def _search_duplicate_compilers(error_cls):
6464#: Map a group of checks to the list of related audit tags
6565GROUPS = collections .defaultdict (list )
6666
67+ # TODO: turn this on (or remove the strict option entirely and make
68+ # prevalidate_variant_value always strict) when we're ready to correct packages with
69+ # invalid variant specifications, e.g. @9:+foo when foo only exists for pkg@:8, or
70+ # ~cuda~cudnn, where cudnn doesn't exist when ~cuda.
71+ strict_variants = False
72+
6773
6874class Error :
6975 """Information on an error reported in a test."""
@@ -315,9 +321,10 @@ def _avoid_mismatched_variants(error_cls):
315321 continue
316322
317323 # Variant cannot accept this value
318- s = spack .spec .Spec (pkg_name )
319324 try :
320- s .update_variant_validate (variant .name , variant .value )
325+ spack .variant .prevalidate_variant_value (
326+ pkg_cls , variant , variant .value , strict = strict_variants
327+ )
321328 except Exception :
322329 summary = (
323330 f"Setting the variant '{ variant .name } ' of the '{ pkg_name } ' package "
@@ -650,9 +657,14 @@ def _ensure_env_methods_are_ported_to_builders(pkgs, error_cls):
650657 errors = []
651658 for pkg_name in pkgs :
652659 pkg_cls = spack .repo .PATH .get_pkg_class (pkg_name )
653- buildsystem_variant , _ = pkg_cls .variants ["build_system" ]
654- buildsystem_names = [getattr (x , "value" , x ) for x in buildsystem_variant .values ]
660+
661+ buildsystem_names = set ()
662+ build_system_variants = [vdef for _ , vdef in pkg_cls .variant_definitions ("build_system" )]
663+ buildsystem_names = set (
664+ getattr (v , "value" , v ) for variant in build_system_variants for v in variant .values
665+ )
655666 builder_cls_names = [spack .builder .BUILDER_CLS [x ].__name__ for x in buildsystem_names ]
667+
656668 module = pkg_cls .module
657669 has_builders_in_package_py = any (
658670 getattr (module , name , False ) for name in builder_cls_names
@@ -837,20 +849,26 @@ def check_virtual_with_variants(spec, msg):
837849
838850 # check variants
839851 dependency_variants = dep .spec .variants
840- for name , value in dependency_variants .items ():
852+ for name , variant in dependency_variants .items ():
841853 try :
842- v , _ = dependency_pkg_cls .variants [name ]
843- v .validate_or_raise (value , pkg_cls = dependency_pkg_cls )
854+ spack .variant .prevalidate_variant_value (
855+ dependency_pkg_cls ,
856+ variant ,
857+ variant .value ,
858+ dep .spec ,
859+ strict = strict_variants ,
860+ )
844861 except Exception as e :
845862 summary = (
846863 f"{ pkg_name } : wrong variant used for dependency in 'depends_on()'"
847864 )
848865
866+ error_msg = str (e )
849867 if isinstance (e , KeyError ):
850868 error_msg = (
851869 f"variant { str (e ).strip ()} does not exist in package { dep_name } "
870+ f" in package '{ dep_name } '"
852871 )
853- error_msg += f" in package '{ dep_name } '"
854872
855873 errors .append (
856874 error_cls (summary = summary , details = [error_msg , f"in { filename } " ])
@@ -862,38 +880,40 @@ def check_virtual_with_variants(spec, msg):
862880@package_directives
863881def _ensure_variant_defaults_are_parsable (pkgs , error_cls ):
864882 """Ensures that variant defaults are present and parsable from cli"""
883+
884+ def check_variant (pkg_name , variant ):
885+ default_is_parsable = (
886+ # Permitting a default that is an instance on 'int' permits
887+ # to have foo=false or foo=0. Other falsish values are
888+ # not allowed, since they can't be parsed from cli ('foo=')
889+ isinstance (variant .default , int )
890+ or variant .default
891+ )
892+ if not default_is_parsable :
893+ msg = f"Variant '{ vname } ' of package '{ pkg_name } ' has a bad default value"
894+ errors .append (error_cls (msg , []))
895+ return
896+
897+ try :
898+ vspec = variant .make_default ()
899+ except spack .variant .MultipleValuesInExclusiveVariantError :
900+ msg = f"Can't create default value for variant '{ vname } ' in package '{ pkg_name } '"
901+ errors .append (error_cls (msg , []))
902+ return
903+
904+ try :
905+ variant .validate_or_raise (vspec , pkg_cls = pkg_cls )
906+ except spack .variant .InvalidVariantValueError :
907+ msg = "Default value of variant '{vname}' in package '{pkg.name}' is invalid"
908+ question = "Is it among the allowed values?"
909+ errors .append (error_cls (msg , [question ]))
910+
865911 errors = []
866912 for pkg_name in pkgs :
867913 pkg_cls = spack .repo .PATH .get_pkg_class (pkg_name )
868- for variant_name , entry in pkg_cls .variants .items ():
869- variant , _ = entry
870- default_is_parsable = (
871- # Permitting a default that is an instance on 'int' permits
872- # to have foo=false or foo=0. Other falsish values are
873- # not allowed, since they can't be parsed from cli ('foo=')
874- isinstance (variant .default , int )
875- or variant .default
876- )
877- if not default_is_parsable :
878- error_msg = "Variant '{}' of package '{}' has a bad default value"
879- errors .append (error_cls (error_msg .format (variant_name , pkg_name ), []))
880- continue
881-
882- try :
883- vspec = variant .make_default ()
884- except spack .variant .MultipleValuesInExclusiveVariantError :
885- error_msg = "Cannot create a default value for the variant '{}' in package '{}'"
886- errors .append (error_cls (error_msg .format (variant_name , pkg_name ), []))
887- continue
888-
889- try :
890- variant .validate_or_raise (vspec , pkg_cls = pkg_cls )
891- except spack .variant .InvalidVariantValueError :
892- error_msg = (
893- "The default value of the variant '{}' in package '{}' failed validation"
894- )
895- question = "Is it among the allowed values?"
896- errors .append (error_cls (error_msg .format (variant_name , pkg_name ), [question ]))
914+ for vname in pkg_cls .variant_names ():
915+ for _ , variant_def in pkg_cls .variant_definitions (vname ):
916+ check_variant (pkg_cls , variant_def )
897917
898918 return errors
899919
@@ -904,11 +924,11 @@ def _ensure_variants_have_descriptions(pkgs, error_cls):
904924 errors = []
905925 for pkg_name in pkgs :
906926 pkg_cls = spack .repo .PATH .get_pkg_class (pkg_name )
907- for variant_name , entry in pkg_cls .variants . items ():
908- variant , _ = entry
909- if not variant .description :
910- error_msg = "Variant '{}' in package '{}' is missing a description"
911- errors .append (error_cls (error_msg . format ( variant_name , pkg_name ) , []))
927+ for name in pkg_cls .variant_names ():
928+ for when , variant in pkg_cls . variant_definitions ( name ):
929+ if not variant .description :
930+ msg = f "Variant '{ name } ' in package '{ pkg_name } ' is missing a description"
931+ errors .append (error_cls (msg , []))
912932
913933 return errors
914934
@@ -965,29 +985,29 @@ def _version_constraints_are_satisfiable_by_some_version_in_repo(pkgs, error_cls
965985
966986
967987def _analyze_variants_in_directive (pkg , constraint , directive , error_cls ):
968- variant_exceptions = (
969- spack .variant .InconsistentValidationError ,
970- spack .variant .MultipleValuesInExclusiveVariantError ,
971- spack .variant .InvalidVariantValueError ,
972- KeyError ,
973- )
974988 errors = []
975- for name , v in constraint .variants .items ():
976- try :
977- variant , _ = pkg .variants [name ]
978- variant .validate_or_raise (v , pkg_cls = pkg )
979- except variant_exceptions as e :
980- summary = pkg .name + ': wrong variant in "{0}" directive'
981- summary = summary .format (directive )
982- filename = spack .repo .PATH .filename_for_package_name (pkg .name )
989+ variant_names = pkg .variant_names ()
983990
984- error_msg = str ( e ). strip ()
985- if isinstance ( e , KeyError ):
986- error_msg = "the variant {0} does not exist" . format ( error_msg )
991+ for name , v in constraint . variants . items ():
992+ summary = f" { pkg . name } : wrong variant in ' { directive } ' directive"
993+ filename = spack . repo . PATH . filename_for_package_name ( pkg . name )
987994
988- err = error_cls (summary = summary , details = [error_msg , "in " + filename ])
995+ if name not in variant_names :
996+ msg = f"variant { name } does not exist in { pkg .name } "
997+ errors .append (error_cls (summary = summary , details = [msg , f"in { filename } " ]))
998+ continue
989999
990- errors .append (err )
1000+ try :
1001+ spack .variant .prevalidate_variant_value (
1002+ pkg , v , v .value , constraint , strict = strict_variants
1003+ )
1004+ except (
1005+ spack .variant .InconsistentValidationError ,
1006+ spack .variant .MultipleValuesInExclusiveVariantError ,
1007+ spack .variant .InvalidVariantValueError ,
1008+ ) as e :
1009+ msg = str (e ).strip ()
1010+ errors .append (error_cls (summary = summary , details = [msg , f"in { filename } " ]))
9911011
9921012 return errors
9931013
@@ -1025,9 +1045,10 @@ def _extracts_errors(triggers, summary):
10251045 for dname in dnames
10261046 )
10271047
1028- for vname , (variant , triggers ) in pkg_cls .variants .items ():
1029- summary = f"{ pkg_name } : wrong 'when=' condition for the '{ vname } ' variant"
1030- errors .extend (_extracts_errors (triggers , summary ))
1048+ for when , variants_by_name in pkg_cls .variants .items ():
1049+ for vname , variant in variants_by_name .items ():
1050+ summary = f"{ pkg_name } : wrong 'when=' condition for the '{ vname } ' variant"
1051+ errors .extend (_extracts_errors ([when ], summary ))
10311052
10321053 for when , providers , details in _error_items (pkg_cls .provided ):
10331054 errors .extend (
0 commit comments