5454ItemKind : TypeAlias = str
5555PackagedWith : TypeAlias = str
5656CtkVersionConstraintArg : TypeAlias = str | SpecifierSet | None
57+ PairwiseItemRelation : TypeAlias = str
5758
5859_CTK_VERSION_RE = re .compile (r"^(?P<major>\d+)\.(?P<minor>\d+)" )
5960_CTK_VERSION_CONSTRAINT_ERROR = (
6061 "ctk_version must be None, a non-empty PEP 440 specifier string like '>=13.2,<14', "
6162 "or a packaging.specifiers.SpecifierSet."
6263)
64+ _PAIRWISE_ITEM_RELATION_NONE = "none"
65+ _PAIRWISE_ITEM_RELATION_EXACT_CTK_MATCH_REQUIRED = "exact-ctk-match-required"
6366
6467_STATIC_LIBS_PACKAGED_WITH : dict [str , PackagedWith ] = {
6568 "cudadevrt" : "ctk" ,
@@ -392,57 +395,133 @@ def _resolve_binary_item(utility_name: str, abs_path: str) -> ResolvedItem:
392395 )
393396
394397
398+ def _unsupported_packaging_message (
399+ item : ResolvedItem , * , allow_compatibility_neutral_driver_libraries : bool = False
400+ ) -> str :
401+ message = "v1 compatibility checks only give definitive answers for packaged_with='ctk' items"
402+ if allow_compatibility_neutral_driver_libraries :
403+ message += ", plus compatibility-neutral driver libraries"
404+ return f"{ message } . { item .describe ()} is packaged_with={ item .packaged_with !r} ."
405+
406+
407+ def _missing_ctk_metadata_message (item : ResolvedItem ) -> str :
408+ return (
409+ "v1 compatibility checks require either an enclosing CUDA Toolkit root "
410+ "with cuda.h or wheel metadata that can be traced to an installed "
411+ f"cuda-toolkit distribution. Could not determine the CTK version for { item .describe ()} ."
412+ )
413+
414+
415+ def _ctk_constraint_failure_message (item : ResolvedItem , constraint : CtkVersionConstraint ) -> str :
416+ assert item .ctk_version is not None
417+ return f"{ item .describe ()} resolves to CTK { item .ctk_version } , which does not satisfy ctk_version{ constraint } ."
418+
419+
420+ def _ctk_pair_mismatch_message (item1 : ResolvedItem , item2 : ResolvedItem ) -> str :
421+ assert item1 .ctk_version is not None
422+ assert item2 .ctk_version is not None
423+ return (
424+ f"{ item1 .describe ()} resolves to CTK { item1 .ctk_version } , while "
425+ f"{ item2 .describe ()} resolves to CTK { item2 .ctk_version } . "
426+ "v1 requires an exact CTK major.minor match."
427+ )
428+
429+
430+ def _driver_major_mismatch_message (driver_cuda_version : DriverCudaVersion , item : ResolvedItem ) -> str :
431+ assert item .ctk_version is not None
432+ return (
433+ f"Driver version { driver_cuda_version .encoded } only supports CUDA major version { driver_cuda_version .major } , "
434+ f"but { item .describe ()} requires CTK { item .ctk_version } . "
435+ "v1 requires driver_major >= ctk_major."
436+ )
437+
438+
439+ def _compatible_pair_message (driver_cuda_version : DriverCudaVersion , item1 : ResolvedItem , item2 : ResolvedItem ) -> str :
440+ assert item1 .ctk_version is not None
441+ return (
442+ f"{ item1 .describe ()} and { item2 .describe ()} both resolve to CTK { item1 .ctk_version } , "
443+ f"and driver version { driver_cuda_version .encoded } satisfies the v1 driver guard rail."
444+ )
445+
446+
447+ def _supported_packaging_result (item : ResolvedItem ) -> CompatibilityResult | None :
448+ if item .packaged_with == "ctk" :
449+ return None
450+ return CompatibilityResult (status = "insufficient_metadata" , message = _unsupported_packaging_message (item ))
451+
452+
453+ def _ctk_metadata_result (item : ResolvedItem ) -> CompatibilityResult | None :
454+ if item .ctk_version is not None and item .ctk_version_source is not None :
455+ return None
456+ return CompatibilityResult (status = "insufficient_metadata" , message = _missing_ctk_metadata_message (item ))
457+
458+
459+ def _classify_pairwise_item_relation (item1 : ResolvedItem , item2 : ResolvedItem ) -> PairwiseItemRelation :
460+ if item1 .packaged_with == "driver" or item2 .packaged_with == "driver" :
461+ return _PAIRWISE_ITEM_RELATION_NONE
462+ return _PAIRWISE_ITEM_RELATION_EXACT_CTK_MATCH_REQUIRED
463+
464+
465+ def _ctk_coherence_result (item1 : ResolvedItem , item2 : ResolvedItem ) -> CompatibilityResult | None :
466+ assert item1 .ctk_version is not None
467+ assert item2 .ctk_version is not None
468+ if item1 .ctk_version == item2 .ctk_version :
469+ return None
470+ return CompatibilityResult (status = "incompatible" , message = _ctk_pair_mismatch_message (item1 , item2 ))
471+
472+
473+ def _pipeline_compatibility_result (_item1 : ResolvedItem , _item2 : ResolvedItem ) -> CompatibilityResult | None :
474+ # v1 has no pipeline-sensitive rules yet, but this separate hook keeps the
475+ # policy surface ready for nvrtc/nvJitLink and nvvm work.
476+ return None
477+
478+
479+ def _pairwise_policy_result (item1 : ResolvedItem , item2 : ResolvedItem ) -> CompatibilityResult | None :
480+ relation = _classify_pairwise_item_relation (item1 , item2 )
481+ if relation == _PAIRWISE_ITEM_RELATION_NONE :
482+ return None
483+ if relation == _PAIRWISE_ITEM_RELATION_EXACT_CTK_MATCH_REQUIRED :
484+ result = _ctk_coherence_result (item1 , item2 )
485+ if result is not None :
486+ return result
487+ return _pipeline_compatibility_result (item1 , item2 )
488+ raise AssertionError (f"Unhandled pairwise item relation: { relation !r} " )
489+
490+
491+ def _driver_compatibility_result (
492+ driver_cuda_version : DriverCudaVersion , item : ResolvedItem
493+ ) -> CompatibilityResult | None :
494+ assert item .ctk_version is not None
495+ if driver_cuda_version .major >= item .ctk_version .major :
496+ return None
497+ return CompatibilityResult (
498+ status = "incompatible" ,
499+ message = _driver_major_mismatch_message (driver_cuda_version , item ),
500+ )
501+
502+
395503def compatibility_check (
396504 driver_cuda_version : DriverCudaVersion , item1 : ResolvedItem , item2 : ResolvedItem
397505) -> CompatibilityResult :
398506 for item in (item1 , item2 ):
399- if item .packaged_with != "ctk" :
400- return CompatibilityResult (
401- status = "insufficient_metadata" ,
402- message = (
403- "v1 compatibility checks only give definitive answers for "
404- f"packaged_with='ctk' items. { item .describe ()} is packaged_with={ item .packaged_with !r} ."
405- ),
406- )
407- if item .ctk_version is None or item .ctk_version_source is None :
408- return CompatibilityResult (
409- status = "insufficient_metadata" ,
410- message = (
411- "v1 compatibility checks require either an enclosing CUDA Toolkit root "
412- "with cuda.h or wheel metadata that can be traced to an installed "
413- f"cuda-toolkit distribution. Could not determine the CTK version for { item .describe ()} ."
414- ),
415- )
507+ result = _supported_packaging_result (item )
508+ if result is not None :
509+ return result
510+ result = _ctk_metadata_result (item )
511+ if result is not None :
512+ return result
416513
417- assert item1 .ctk_version is not None
418- assert item2 .ctk_version is not None
514+ result = _pairwise_policy_result (item1 , item2 )
515+ if result is not None :
516+ return result
419517
420- if item1 .ctk_version != item2 .ctk_version :
421- return CompatibilityResult (
422- status = "incompatible" ,
423- message = (
424- f"{ item1 .describe ()} resolves to CTK { item1 .ctk_version } , while "
425- f"{ item2 .describe ()} resolves to CTK { item2 .ctk_version } . "
426- "v1 requires an exact CTK major.minor match."
427- ),
428- )
429-
430- if driver_cuda_version .major < item1 .ctk_version .major :
431- return CompatibilityResult (
432- status = "incompatible" ,
433- message = (
434- f"Driver version { driver_cuda_version .encoded } only supports CUDA major version { driver_cuda_version .major } , "
435- f"but { item1 .describe ()} requires CTK { item1 .ctk_version } . "
436- "v1 requires driver_major >= ctk_major."
437- ),
438- )
518+ result = _driver_compatibility_result (driver_cuda_version , item1 )
519+ if result is not None :
520+ return result
439521
440522 return CompatibilityResult (
441523 status = "compatible" ,
442- message = (
443- f"{ item1 .describe ()} and { item2 .describe ()} both resolve to CTK { item1 .ctk_version } , "
444- f"and driver version { driver_cuda_version .encoded } satisfies the v1 driver guard rail."
445- ),
524+ message = _compatible_pair_message (driver_cuda_version , item1 , item2 ),
446525 )
447526
448527
@@ -474,33 +553,31 @@ def _enforce_supported_packaging(self, item: ResolvedItem) -> None:
474553 if item .packaged_with == "ctk" :
475554 return
476555 raise CompatibilityInsufficientMetadataError (
477- "v1 compatibility checks only give definitive answers for "
478- f"packaged_with='ctk' items, plus compatibility-neutral driver libraries. "
479- f"{ item .describe ()} is packaged_with={ item .packaged_with !r} ."
556+ _unsupported_packaging_message (item , allow_compatibility_neutral_driver_libraries = True )
480557 )
481558
482559 def _enforce_ctk_metadata (self , item : ResolvedItem ) -> None :
483- if item .ctk_version is not None and item .ctk_version_source is not None :
560+ result = _ctk_metadata_result (item )
561+ if result is None :
484562 return
485- raise CompatibilityInsufficientMetadataError (
486- "v1 compatibility checks require either an enclosing CUDA Toolkit root "
487- "with cuda.h or wheel metadata that can be traced to an installed "
488- f"cuda-toolkit distribution. Could not determine the CTK version for { item .describe ()} ."
489- )
563+ result .require_compatible ()
490564
491565 def _enforce_constraints (self , item : ResolvedItem ) -> None :
492566 assert item .ctk_version is not None
493567 if self ._ctk_version_constraint is not None and not self ._ctk_version_constraint .matches (item .ctk_version ):
494- raise CompatibilityCheckError (
495- f"{ item .describe ()} resolves to CTK { item .ctk_version } , which does not satisfy "
496- f"ctk_version{ self ._ctk_version_constraint } ."
497- )
568+ raise CompatibilityCheckError (_ctk_constraint_failure_message (item , self ._ctk_version_constraint ))
498569
499- def _anchor_item (self ) -> ResolvedItem | None :
500- for item in self ._resolved_items :
501- if item .packaged_with == "ctk" :
502- return item
503- return None
570+ def _enforce_driver_compatibility (self , item : ResolvedItem ) -> None :
571+ result = _driver_compatibility_result (self ._get_driver_cuda_version (), item )
572+ if result is None :
573+ return
574+ result .require_compatible ()
575+
576+ def _enforce_pairwise_compatibility (self , prior_item : ResolvedItem , item : ResolvedItem ) -> None :
577+ result = _pairwise_policy_result (prior_item , item )
578+ if result is None :
579+ return
580+ result .require_compatible ()
504581
505582 def _remember (self , item : ResolvedItem ) -> None :
506583 if item not in self ._resolved_items :
@@ -520,10 +597,9 @@ def _register_and_check(self, item: ResolvedItem) -> None:
520597 self ._enforce_supported_packaging (item )
521598 self ._enforce_ctk_metadata (item )
522599 self ._enforce_constraints (item )
523- anchor = self ._anchor_item ()
524- if anchor is None :
525- anchor = item
526- compatibility_check (self ._get_driver_cuda_version (), anchor , item ).require_compatible ()
600+ for prior_item in self ._resolved_items :
601+ self ._enforce_pairwise_compatibility (prior_item , item )
602+ self ._enforce_driver_compatibility (item )
527603 self ._remember (item )
528604
529605 def load_nvidia_dynamic_lib (self , libname : str ) -> LoadedDL :
0 commit comments