python.tools.prereqs.requirements.base

# SPDX-FileCopyrightText: Copyright (c) 2025-present NVIDIA CORPORATION & AFFILIATES.
# All rights reserved.
# SPDX-License-Identifier: BSD-3-Clause
"""
Base classes for requirement types.

This module provides the foundational classes for all dependency requirements.
Each requirement knows how to format its status and determine if it represents a failure.
"""

from abc import ABC, abstractmethod
from typing import Optional, Dict
from dataclasses import dataclass

from ..colors import colorize

@dataclass
class RequirementStatus:
    """Validation status constants."""

    SUCCESS = "SUCCESS"
    NOT_FOUND = "NOT_FOUND"
    INCOMPATIBLE = "INCOMPATIBLE"

class Requirement(ABC):
    """
    Base class for all requirement types.

    All requirements must implement:
    - format_status_line(): Format output for terminal
    - is_failure(): Determine if this represents a build failure
    """

    def __init__(
        self,
        name: str,
        cmake_vars: Dict,
        found_var: str,
        status_var: str,
        optional_var: str,
        location_var: Optional[str] = None,
    ):
        """
        Initialize from CMake variable names.

        Args:
            name: Dependency name
            cmake_vars: Dictionary of all CMake variables
            found_var: Name of CMake variable for found status (e.g., "Python_FOUND")
            status_var: Name of CMake variable for validation status (e.g., "Python_STATUS")
            optional_var: Name of CMake variable for optional flag (e.g., "NVFUSER_REQUIREMENT_Python_OPTIONAL")
            location_var: Optional name of CMake variable for location (e.g., "Python_EXECUTABLE")
        """
        self.name = name
        self.found = self._to_bool(cmake_vars.get(found_var, "FALSE"))
        self.status = cmake_vars.get(status_var, "UNKNOWN")
        self.optional = self._to_bool(cmake_vars.get(optional_var, "FALSE"))

        # Look up location if location_var is provided
        self.location = cmake_vars.get(location_var) if location_var else None

    @staticmethod
    def _to_bool(value) -> bool:
        """Convert CMake boolean string to Python bool."""
        if isinstance(value, bool):
            return value
        if isinstance(value, str):
            return value.upper() in ("TRUE", "ON", "YES", "1")
        return bool(value)

    @abstractmethod
    def format_status_line(self, colors) -> str:
        """Format terminal status line for this requirement."""
        pass

    def is_failure(self) -> bool:
        """Check if this requirement represents a failure."""
        return not self.optional and self.status != RequirementStatus.SUCCESS

    def get_failure_data(self):
        """Get data for help text generation."""
        # Return a dict compatible with help system
        return {
            "name": self.name,
            "status": self.status,
            "found": self.found,
            "optional": self.optional,
            "location": self.location,
        }

    @abstractmethod
    def generate_help(self, platform_info):
        """Generate help text for this requirement when it fails.

        Subclasses should override this to provide specific installation instructions.
        """
        pass  # Default: no help text

class VersionRequirement(Requirement):
    """
    Base class for requirements with version checking.

    Provides standard version display formatting.
    Subclasses inherit all version comparison logic.
    """

    def __init__(
        self,
        name: str,
        cmake_vars: Dict,
        found_var: str,
        status_var: str,
        optional_var: str,
        version_found_var: Optional[str] = None,
        version_required_var: Optional[str] = None,
        location_var: Optional[str] = None,
    ):
        """
        Initialize version requirement.

        Args:
            name: Dependency name
            cmake_vars: Dictionary of all CMake variables
            found_var: Name of CMake variable for found status
            status_var: Name of CMake variable for validation status
            optional_var: Name of CMake variable for optional flag
            version_found_var: Name of CMake variable for detected version (e.g., "Python_VERSION")
            version_required_var: Name of CMake variable for minimum required version (e.g., "NVFUSER_REQUIREMENT_Python_VERSION_MIN")
            location_var: Optional name of CMake variable for location
        """
        super().__init__(
            name, cmake_vars, found_var, status_var, optional_var, location_var
        )
        self.version_found = (
            cmake_vars.get(version_found_var) if version_found_var else None
        )
        self.version_required = (
            cmake_vars.get(version_required_var) if version_required_var else None
        )

    def format_status_line(self, colors) -> str:
        """Format status line with version information."""
        if self.status == RequirementStatus.SUCCESS:
            return self._format_success(colors)
        elif self.status == RequirementStatus.NOT_FOUND:
            return self._format_not_found(colors)
        elif self.status == RequirementStatus.INCOMPATIBLE:
            return self._format_incompatible(colors)
        else:
            return colorize(colors.BOLD_RED, f"[nvFuser] ✗ {self.name} unknown status")

    def _format_success(self, colors) -> str:
        """For example:
        Format success: [nvFuser] ✓ Python        3.12.3 >= 3.10 (/usr/bin/python3)
        """
        # Add asterisk for optional requirements
        name_with_marker = f"{self.name}*" if self.optional else self.name
        # Status symbol and name in white/green with padding
        name_padded = f"{name_with_marker:<15}"  # Left-align with 15 char width
        status_part = colorize(colors.GREEN, "[nvFuser] ✓ ") + name_padded

        # Version info in green
        if self.version_found and self.version_required:
            version_part = colorize(
                colors.GREEN, f"{self.version_found} >= {self.version_required}"
            )
        elif self.version_found:
            version_part = colorize(colors.GREEN, self.version_found)
        else:
            version_part = ""

        # Combine parts
        if version_part:
            main_line = f"{status_part} {version_part}"
        else:
            main_line = status_part

        # Add location in cyan if available
        if self.location:
            main_line += " " + colorize(colors.CYAN, f"({self.location})")

        return main_line

    def _format_not_found(self, colors) -> str:
        """Format not found line."""
        # Add asterisk for optional requirements
        name_with_marker = f"{self.name}*" if self.optional else self.name
        name_padded = f"{name_with_marker:<15}"  # Left-align with 15 char width

        if self.optional:
            status_part = colorize(colors.YELLOW, "[nvFuser] ○ ") + name_padded
            if self.version_required:
                return (
                    status_part
                    + " "
                    + colorize(
                        colors.YELLOW,
                        f"Not found (optional, v{self.version_required}+ recommended)",
                    )
                )
            else:
                return (
                    status_part + " " + colorize(colors.YELLOW, "Not found (optional)")
                )
        else:
            status_part = colorize(colors.BOLD_RED, "[nvFuser] ✗ ") + name_padded
            if self.version_required:
                return (
                    status_part
                    + " "
                    + colorize(
                        colors.BOLD_RED,
                        f"Not found (requires {self.version_required}+)",
                    )
                )
            else:
                return status_part + " " + colorize(colors.BOLD_RED, "Not found")

    def _format_incompatible(self, colors) -> str:
        """Format incompatible: [nvFuser] ✗ Python        3.7.0 < 3.8"""
        # Add asterisk for optional requirements
        name_with_marker = f"{self.name}*" if self.optional else self.name
        name_padded = f"{name_with_marker:<15}"  # Left-align with 15 char width
        status_part = colorize(colors.BOLD_RED, "[nvFuser] ✗ ") + name_padded

        if self.version_found and self.version_required:
            return (
                status_part
                + " "
                + colorize(
                    colors.BOLD_RED, f"{self.version_found} < {self.version_required}"
                )
            )
        else:
            return status_part + " " + colorize(colors.BOLD_RED, "incompatible")

class BooleanRequirement(Requirement):
    """
    Base class for requirements without version checking.

    Simple pass/fail validation (Git submodules, Ninja).
    """

    def format_status_line(self, colors) -> str:
        """Format status line without version information."""
        # Add asterisk for optional requirements
        name_with_marker = f"{self.name}*" if self.optional else self.name
        name_padded = f"{name_with_marker:<15}"  # Left-align with 15 char width

        if self.status == RequirementStatus.SUCCESS:
            status_part = colorize(colors.GREEN, "[nvFuser] ✓ ") + name_padded
            if self.location:
                return status_part + " " + colorize(colors.CYAN, f"({self.location})")
            return status_part
        elif self.status == RequirementStatus.NOT_FOUND:
            if self.optional:
                status_part = colorize(colors.YELLOW, "[nvFuser] ○ ") + name_padded
                return (
                    status_part + " " + colorize(colors.YELLOW, "Not found (optional)")
                )
            else:
                status_part = colorize(colors.BOLD_RED, "[nvFuser] ✗ ") + name_padded
                return status_part + " " + colorize(colors.BOLD_RED, "Not found")
        else:
            return (
                colorize(colors.BOLD_RED, "[nvFuser] ✗ ")
                + name_padded
                + " "
                + colorize(colors.BOLD_RED, "validation failed")
            )


RequirementStatus

# SPDX-FileCopyrightText: Copyright (c) 2025-present NVIDIA CORPORATION & AFFILIATES.
# All rights reserved.
# SPDX-License-Identifier: BSD-3-Clause
"""
Base classes for requirement types.

This module provides the foundational classes for all dependency requirements.
Each requirement knows how to format its status and determine if it represents a failure.
"""

from abc import ABC, abstractmethod
from typing import Optional, Dict
from dataclasses import dataclass

from ..colors import colorize

@dataclass
class RequirementStatus:
    """Validation status constants."""

    SUCCESS = "SUCCESS"
    NOT_FOUND = "NOT_FOUND"
    INCOMPATIBLE = "INCOMPATIBLE"

class Requirement(ABC):
    """
    Base class for all requirement types.

    All requirements must implement:
    - format_status_line(): Format output for terminal
    - is_failure(): Determine if this represents a build failure
    """

    def __init__(
        self,
        name: str,
        cmake_vars: Dict,
        found_var: str,
        status_var: str,
        optional_var: str,
        location_var: Optional[str] = None,
    ):


Requirement

This module provides the foundational classes for all dependency requirements.
Each requirement knows how to format its status and determine if it represents a failure.
"""

from abc import ABC, abstractmethod
from typing import Optional, Dict
from dataclasses import dataclass

from ..colors import colorize

@dataclass
class RequirementStatus:
    """Validation status constants."""

    SUCCESS = "SUCCESS"
    NOT_FOUND = "NOT_FOUND"
    INCOMPATIBLE = "INCOMPATIBLE"

class Requirement(ABC):
    """
    Base class for all requirement types.

    All requirements must implement:
    - format_status_line(): Format output for terminal
    - is_failure(): Determine if this represents a build failure
    """

    def __init__(
        self,
        name: str,
        cmake_vars: Dict,
        found_var: str,
        status_var: str,
        optional_var: str,
        location_var: Optional[str] = None,
    ):
        """
        Initialize from CMake variable names.

        Args:
            name: Dependency name
            cmake_vars: Dictionary of all CMake variables
            found_var: Name of CMake variable for found status (e.g., "Python_FOUND")
            status_var: Name of CMake variable for validation status (e.g., "Python_STATUS")
            optional_var: Name of CMake variable for optional flag (e.g., "NVFUSER_REQUIREMENT_Python_OPTIONAL")
            location_var: Optional name of CMake variable for location (e.g., "Python_EXECUTABLE")
        """
        self.name = name
        self.found = self._to_bool(cmake_vars.get(found_var, "FALSE"))
        self.status = cmake_vars.get(status_var, "UNKNOWN")
        self.optional = self._to_bool(cmake_vars.get(optional_var, "FALSE"))

        # Look up location if location_var is provided
        self.location = cmake_vars.get(location_var) if location_var else None

    @staticmethod
    def _to_bool(value) -> bool:
        """Convert CMake boolean string to Python bool."""
        if isinstance(value, bool):
            return value
        if isinstance(value, str):
            return value.upper() in ("TRUE", "ON", "YES", "1")
        return bool(value)

    @abstractmethod
    def format_status_line(self, colors) -> str:
        """Format terminal status line for this requirement."""
        pass

    def is_failure(self) -> bool:
        """Check if this requirement represents a failure."""
        return not self.optional and self.status != RequirementStatus.SUCCESS

    def get_failure_data(self):
        """Get data for help text generation."""
        # Return a dict compatible with help system
        return {
            "name": self.name,
            "status": self.status,
            "found": self.found,
            "optional": self.optional,
            "location": self.location,
        }

    @abstractmethod
    def generate_help(self, platform_info):
        """Generate help text for this requirement when it fails.

        Subclasses should override this to provide specific installation instructions.
        """
        pass  # Default: no help text

class VersionRequirement(Requirement):
    """
    Base class for requirements with version checking.

    Provides standard version display formatting.
    Subclasses inherit all version comparison logic.
    """

    def __init__(
        self,
        name: str,
        cmake_vars: Dict,
        found_var: str,
        status_var: str,
        optional_var: str,
        version_found_var: Optional[str] = None,
        version_required_var: Optional[str] = None,
        location_var: Optional[str] = None,


VersionRequirement

    def get_failure_data(self):
        """Get data for help text generation."""
        # Return a dict compatible with help system
        return {
            "name": self.name,
            "status": self.status,
            "found": self.found,
            "optional": self.optional,
            "location": self.location,
        }

    @abstractmethod
    def generate_help(self, platform_info):
        """Generate help text for this requirement when it fails.

        Subclasses should override this to provide specific installation instructions.
        """
        pass  # Default: no help text

class VersionRequirement(Requirement):
    """
    Base class for requirements with version checking.

    Provides standard version display formatting.
    Subclasses inherit all version comparison logic.
    """

    def __init__(
        self,
        name: str,
        cmake_vars: Dict,
        found_var: str,
        status_var: str,
        optional_var: str,
        version_found_var: Optional[str] = None,
        version_required_var: Optional[str] = None,
        location_var: Optional[str] = None,
    ):
        """
        Initialize version requirement.

        Args:
            name: Dependency name
            cmake_vars: Dictionary of all CMake variables
            found_var: Name of CMake variable for found status
            status_var: Name of CMake variable for validation status
            optional_var: Name of CMake variable for optional flag
            version_found_var: Name of CMake variable for detected version (e.g., "Python_VERSION")
            version_required_var: Name of CMake variable for minimum required version (e.g., "NVFUSER_REQUIREMENT_Python_VERSION_MIN")
            location_var: Optional name of CMake variable for location
        """
        super().__init__(
            name, cmake_vars, found_var, status_var, optional_var, location_var
        )
        self.version_found = (
            cmake_vars.get(version_found_var) if version_found_var else None
        )
        self.version_required = (
            cmake_vars.get(version_required_var) if version_required_var else None
        )

    def format_status_line(self, colors) -> str:
        """Format status line with version information."""
        if self.status == RequirementStatus.SUCCESS:
            return self._format_success(colors)
        elif self.status == RequirementStatus.NOT_FOUND:
            return self._format_not_found(colors)
        elif self.status == RequirementStatus.INCOMPATIBLE:
            return self._format_incompatible(colors)
        else:
            return colorize(colors.BOLD_RED, f"[nvFuser] ✗ {self.name} unknown status")

    def _format_success(self, colors) -> str:
        """For example:
        Format success: [nvFuser] ✓ Python        3.12.3 >= 3.10 (/usr/bin/python3)
        """
        # Add asterisk for optional requirements
        name_with_marker = f"{self.name}*" if self.optional else self.name
        # Status symbol and name in white/green with padding
        name_padded = f"{name_with_marker:<15}"  # Left-align with 15 char width
        status_part = colorize(colors.GREEN, "[nvFuser] ✓ ") + name_padded

        # Version info in green
        if self.version_found and self.version_required:
            version_part = colorize(
                colors.GREEN, f"{self.version_found} >= {self.version_required}"
            )
        elif self.version_found:
            version_part = colorize(colors.GREEN, self.version_found)
        else:
            version_part = ""

        # Combine parts
        if version_part:
            main_line = f"{status_part} {version_part}"
        else:
            main_line = status_part

        # Add location in cyan if available
        if self.location:
            main_line += " " + colorize(colors.CYAN, f"({self.location})")

        return main_line

    def _format_not_found(self, colors) -> str:
        """Format not found line."""
        # Add asterisk for optional requirements
        name_with_marker = f"{self.name}*" if self.optional else self.name
        name_padded = f"{name_with_marker:<15}"  # Left-align with 15 char width

        if self.optional:
            status_part = colorize(colors.YELLOW, "[nvFuser] ○ ") + name_padded
            if self.version_required:
                return (
                    status_part
                    + " "
                    + colorize(
                        colors.YELLOW,
                        f"Not found (optional, v{self.version_required}+ recommended)",
                    )
                )
            else:
                return (
                    status_part + " " + colorize(colors.YELLOW, "Not found (optional)")
                )
        else:
            status_part = colorize(colors.BOLD_RED, "[nvFuser] ✗ ") + name_padded
            if self.version_required:
                return (
                    status_part
                    + " "
                    + colorize(
                        colors.BOLD_RED,
                        f"Not found (requires {self.version_required}+)",
                    )
                )
            else:
                return status_part + " " + colorize(colors.BOLD_RED, "Not found")

    def _format_incompatible(self, colors) -> str:
        """Format incompatible: [nvFuser] ✗ Python        3.7.0 < 3.8"""
        # Add asterisk for optional requirements
        name_with_marker = f"{self.name}*" if self.optional else self.name
        name_padded = f"{name_with_marker:<15}"  # Left-align with 15 char width
        status_part = colorize(colors.BOLD_RED, "[nvFuser] ✗ ") + name_padded

        if self.version_found and self.version_required:
            return (
                status_part
                + " "
                + colorize(
                    colors.BOLD_RED, f"{self.version_found} < {self.version_required}"
                )
            )
        else:
            return status_part + " " + colorize(colors.BOLD_RED, "incompatible")

class BooleanRequirement(Requirement):
    """
    Base class for requirements without version checking.

    Simple pass/fail validation (Git submodules, Ninja).
    """

    def format_status_line(self, colors) -> str:
        """Format status line without version information."""
        # Add asterisk for optional requirements
        name_with_marker = f"{self.name}*" if self.optional else self.name
        name_padded = f"{name_with_marker:<15}"  # Left-align with 15 char width

        if self.status == RequirementStatus.SUCCESS:
            status_part = colorize(colors.GREEN, "[nvFuser] ✓ ") + name_padded
            if self.location:
                return status_part + " " + colorize(colors.CYAN, f"({self.location})")
            return status_part


BooleanRequirement

    def _format_incompatible(self, colors) -> str:
        """Format incompatible: [nvFuser] ✗ Python        3.7.0 < 3.8"""
        # Add asterisk for optional requirements
        name_with_marker = f"{self.name}*" if self.optional else self.name
        name_padded = f"{name_with_marker:<15}"  # Left-align with 15 char width
        status_part = colorize(colors.BOLD_RED, "[nvFuser] ✗ ") + name_padded

        if self.version_found and self.version_required:
            return (
                status_part
                + " "
                + colorize(
                    colors.BOLD_RED, f"{self.version_found} < {self.version_required}"
                )
            )
        else:
            return status_part + " " + colorize(colors.BOLD_RED, "incompatible")

class BooleanRequirement(Requirement):
    """
    Base class for requirements without version checking.

    Simple pass/fail validation (Git submodules, Ninja).
    """

    def format_status_line(self, colors) -> str:
        """Format status line without version information."""
        # Add asterisk for optional requirements
        name_with_marker = f"{self.name}*" if self.optional else self.name
        name_padded = f"{name_with_marker:<15}"  # Left-align with 15 char width

        if self.status == RequirementStatus.SUCCESS:
            status_part = colorize(colors.GREEN, "[nvFuser] ✓ ") + name_padded
            if self.location:
                return status_part + " " + colorize(colors.CYAN, f"({self.location})")
            return status_part
        elif self.status == RequirementStatus.NOT_FOUND:
            if self.optional:
                status_part = colorize(colors.YELLOW, "[nvFuser] ○ ") + name_padded
                return (
                    status_part + " " + colorize(colors.YELLOW, "Not found (optional)")
                )
            else:
                status_part = colorize(colors.BOLD_RED, "[nvFuser] ✗ ") + name_padded
                return status_part + " " + colorize(colors.BOLD_RED, "Not found")
        else:
            return (
                colorize(colors.BOLD_RED, "[nvFuser] ✗ ")
                + name_padded
                + " "
                + colorize(colors.BOLD_RED, "validation failed")
            )


            "location": self.location,
        }

    @abstractmethod
    def generate_help(self, platform_info):
        """Generate help text for this requirement when it fails.

        Subclasses should override this to provide specific installation instructions.
        """
        pass  # Default: no help text

class VersionRequirement(Requirement):
    """
    Base class for requirements with version checking.

    Provides standard version display formatting.
    Subclasses inherit all version comparison logic.
    """

    def __init__(
        self,
        name: str,
        cmake_vars: Dict,
        found_var: str,
        status_var: str,
        optional_var: str,
        version_found_var: Optional[str] = None,
        version_required_var: Optional[str] = None,
        location_var: Optional[str] = None,
    ):
        """
        Initialize version requirement.

        Args:
            name: Dependency name
            cmake_vars: Dictionary of all CMake variables
            found_var: Name of CMake variable for found status
            status_var: Name of CMake variable for validation status
            optional_var: Name of CMake variable for optional flag
            version_found_var: Name of CMake variable for detected version (e.g., "Python_VERSION")
            version_required_var: Name of CMake variable for minimum required version (e.g., "NVFUSER_REQUIREMENT_Python_VERSION_MIN")
            location_var: Optional name of CMake variable for location
        """
        super().__init__(
            name, cmake_vars, found_var, status_var, optional_var, location_var
        )
        self.version_found = (
            cmake_vars.get(version_found_var) if version_found_var else None
        )
        self.version_required = (
            cmake_vars.get(version_required_var) if version_required_var else None
        )

    def format_status_line(self, colors) -> str:
        """Format status line with version information."""
        if self.status == RequirementStatus.SUCCESS:
            return self._format_success(colors)
        elif self.status == RequirementStatus.NOT_FOUND:
            return self._format_not_found(colors)
        elif self.status == RequirementStatus.INCOMPATIBLE:
            return self._format_incompatible(colors)
        else:
            return colorize(colors.BOLD_RED, f"[nvFuser] ✗ {self.name} unknown status")

    def _format_success(self, colors) -> str:
        """For example:
        Format success: [nvFuser] ✓ Python        3.12.3 >= 3.10 (/usr/bin/python3)
        """
        # Add asterisk for optional requirements
        name_with_marker = f"{self.name}*" if self.optional else self.name
        # Status symbol and name in white/green with padding
        name_padded = f"{name_with_marker:<15}"  # Left-align with 15 char width


        """
        Initialize from CMake variable names.

        Args:
            name: Dependency name
            cmake_vars: Dictionary of all CMake variables
            found_var: Name of CMake variable for found status (e.g., "Python_FOUND")
            status_var: Name of CMake variable for validation status (e.g., "Python_STATUS")
            optional_var: Name of CMake variable for optional flag (e.g., "NVFUSER_REQUIREMENT_Python_OPTIONAL")
            location_var: Optional name of CMake variable for location (e.g., "Python_EXECUTABLE")
        """
        self.name = name
        self.found = self._to_bool(cmake_vars.get(found_var, "FALSE"))
        self.status = cmake_vars.get(status_var, "UNKNOWN")
        self.optional = self._to_bool(cmake_vars.get(optional_var, "FALSE"))

        # Look up location if location_var is provided
        self.location = cmake_vars.get(location_var) if location_var else None

    @staticmethod
    def _to_bool(value) -> bool:
        """Convert CMake boolean string to Python bool."""
        if isinstance(value, bool):
            return value
        if isinstance(value, str):
            return value.upper() in ("TRUE", "ON", "YES", "1")
        return bool(value)

    @abstractmethod
    def format_status_line(self, colors) -> str:
        """Format terminal status line for this requirement."""
        pass

    def is_failure(self) -> bool:
        """Check if this requirement represents a failure."""
        return not self.optional and self.status != RequirementStatus.SUCCESS

    def get_failure_data(self):
        """Get data for help text generation."""
        # Return a dict compatible with help system
        return {
            "name": self.name,
            "status": self.status,
            "found": self.found,
            "optional": self.optional,
            "location": self.location,
        }


        if self.version_found and self.version_required:
            return (
                status_part
                + " "
                + colorize(
                    colors.BOLD_RED, f"{self.version_found} < {self.version_required}"
                )
            )
        else:
            return status_part + " " + colorize(colors.BOLD_RED, "incompatible")

class BooleanRequirement(Requirement):
    """
    Base class for requirements without version checking.

    Simple pass/fail validation (Git submodules, Ninja).
    """

    def format_status_line(self, colors) -> str:
        """Format status line without version information."""
        # Add asterisk for optional requirements
        name_with_marker = f"{self.name}*" if self.optional else self.name
        name_padded = f"{name_with_marker:<15}"  # Left-align with 15 char width

        if self.status == RequirementStatus.SUCCESS:
            status_part = colorize(colors.GREEN, "[nvFuser] ✓ ") + name_padded
            if self.location:
                return status_part + " " + colorize(colors.CYAN, f"({self.location})")
            return status_part
        elif self.status == RequirementStatus.NOT_FOUND:
            if self.optional:
                status_part = colorize(colors.YELLOW, "[nvFuser] ○ ") + name_padded
                return (
                    status_part + " " + colorize(colors.YELLOW, "Not found (optional)")
                )
            else:
                status_part = colorize(colors.BOLD_RED, "[nvFuser] ✗ ") + name_padded
                return status_part + " " + colorize(colors.BOLD_RED, "Not found")
        else:
            return (
                colorize(colors.BOLD_RED, "[nvFuser] ✗ ")
                + name_padded
                + " "
                + colorize(colors.BOLD_RED, "validation failed")
            )


        self.status = cmake_vars.get(status_var, "UNKNOWN")
        self.optional = self._to_bool(cmake_vars.get(optional_var, "FALSE"))

        # Look up location if location_var is provided
        self.location = cmake_vars.get(location_var) if location_var else None

    @staticmethod
    def _to_bool(value) -> bool:
        """Convert CMake boolean string to Python bool."""
        if isinstance(value, bool):
            return value
        if isinstance(value, str):
            return value.upper() in ("TRUE", "ON", "YES", "1")
        return bool(value)

    @abstractmethod
    def format_status_line(self, colors) -> str:
        """Format terminal status line for this requirement."""
        pass

    def is_failure(self) -> bool:
        """Check if this requirement represents a failure."""
        return not self.optional and self.status != RequirementStatus.SUCCESS

    def get_failure_data(self):
        """Get data for help text generation."""
        # Return a dict compatible with help system
        return {
            "name": self.name,
            "status": self.status,
            "found": self.found,
            "optional": self.optional,
            "location": self.location,
        }

    @abstractmethod
    def generate_help(self, platform_info):
        """Generate help text for this requirement when it fails.

        Subclasses should override this to provide specific installation instructions.
        """
        pass  # Default: no help text


        self.location = cmake_vars.get(location_var) if location_var else None

    @staticmethod
    def _to_bool(value) -> bool:
        """Convert CMake boolean string to Python bool."""
        if isinstance(value, bool):
            return value
        if isinstance(value, str):
            return value.upper() in ("TRUE", "ON", "YES", "1")
        return bool(value)

    @abstractmethod
    def format_status_line(self, colors) -> str:
        """Format terminal status line for this requirement."""
        pass

    def is_failure(self) -> bool:
        """Check if this requirement represents a failure."""
        return not self.optional and self.status != RequirementStatus.SUCCESS

    def get_failure_data(self):
        """Get data for help text generation."""
        # Return a dict compatible with help system
        return {
            "name": self.name,
            "status": self.status,
            "found": self.found,
            "optional": self.optional,
            "location": self.location,
        }

    @abstractmethod
    def generate_help(self, platform_info):
        """Generate help text for this requirement when it fails.

        Subclasses should override this to provide specific installation instructions.
        """
        pass  # Default: no help text

class VersionRequirement(Requirement):
    """
    Base class for requirements with version checking.

    Provides standard version display formatting.
    Subclasses inherit all version comparison logic.
    """

    def __init__(
        self,


    def format_status_line(self, colors) -> str:
        """Format terminal status line for this requirement."""
        pass

    def is_failure(self) -> bool:
        """Check if this requirement represents a failure."""
        return not self.optional and self.status != RequirementStatus.SUCCESS

    def get_failure_data(self):
        """Get data for help text generation."""
        # Return a dict compatible with help system
        return {
            "name": self.name,
            "status": self.status,
            "found": self.found,
            "optional": self.optional,
            "location": self.location,
        }

    @abstractmethod
    def generate_help(self, platform_info):
        """Generate help text for this requirement when it fails.

        Subclasses should override this to provide specific installation instructions.
        """
        pass  # Default: no help text

class VersionRequirement(Requirement):
    """
    Base class for requirements with version checking.

    Provides standard version display formatting.
    Subclasses inherit all version comparison logic.
    """

    def __init__(
        self,
        name: str,
        cmake_vars: Dict,
        found_var: str,
        status_var: str,
        optional_var: str,
        version_found_var: Optional[str] = None,
        version_required_var: Optional[str] = None,
        location_var: Optional[str] = None,


            name, cmake_vars, found_var, status_var, optional_var, location_var
        )
        self.version_found = (
            cmake_vars.get(version_found_var) if version_found_var else None
        )
        self.version_required = (
            cmake_vars.get(version_required_var) if version_required_var else None
        )

    def format_status_line(self, colors) -> str:
        """Format status line with version information."""
        if self.status == RequirementStatus.SUCCESS:
            return self._format_success(colors)
        elif self.status == RequirementStatus.NOT_FOUND:
            return self._format_not_found(colors)
        elif self.status == RequirementStatus.INCOMPATIBLE:
            return self._format_incompatible(colors)
        else:
            return colorize(colors.BOLD_RED, f"[nvFuser] ✗ {self.name} unknown status")

    def _format_success(self, colors) -> str:
        """For example:
        Format success: [nvFuser] ✓ Python        3.12.3 >= 3.10 (/usr/bin/python3)
        """
        # Add asterisk for optional requirements
        name_with_marker = f"{self.name}*" if self.optional else self.name
        # Status symbol and name in white/green with padding
        name_padded = f"{name_with_marker:<15}"  # Left-align with 15 char width
        status_part = colorize(colors.GREEN, "[nvFuser] ✓ ") + name_padded

        # Version info in green
        if self.version_found and self.version_required:
            version_part = colorize(
                colors.GREEN, f"{self.version_found} >= {self.version_required}"
            )
        elif self.version_found:
            version_part = colorize(colors.GREEN, self.version_found)
        else:
            version_part = ""

        # Combine parts
        if version_part:
            main_line = f"{status_part} {version_part}"
        else:
            main_line = status_part

        # Add location in cyan if available
        if self.location:
            main_line += " " + colorize(colors.CYAN, f"({self.location})")

        return main_line

    def _format_not_found(self, colors) -> str:
        """Format not found line."""
        # Add asterisk for optional requirements
        name_with_marker = f"{self.name}*" if self.optional else self.name
        name_padded = f"{name_with_marker:<15}"  # Left-align with 15 char width

        if self.optional:
            status_part = colorize(colors.YELLOW, "[nvFuser] ○ ") + name_padded
            if self.version_required:
                return (
                    status_part
                    + " "
                    + colorize(
                        colors.YELLOW,
                        f"Not found (optional, v{self.version_required}+ recommended)",
                    )
                )
            else:
                return (


            version_part = colorize(
                colors.GREEN, f"{self.version_found} >= {self.version_required}"
            )
        elif self.version_found:
            version_part = colorize(colors.GREEN, self.version_found)
        else:
            version_part = ""

        # Combine parts
        if version_part:
            main_line = f"{status_part} {version_part}"
        else:
            main_line = status_part

        # Add location in cyan if available
        if self.location:
            main_line += " " + colorize(colors.CYAN, f"({self.location})")

        return main_line

    def _format_not_found(self, colors) -> str:
        """Format not found line."""
        # Add asterisk for optional requirements
        name_with_marker = f"{self.name}*" if self.optional else self.name
        name_padded = f"{name_with_marker:<15}"  # Left-align with 15 char width

        if self.optional:
            status_part = colorize(colors.YELLOW, "[nvFuser] ○ ") + name_padded
            if self.version_required:
                return (
                    status_part
                    + " "
                    + colorize(
                        colors.YELLOW,
                        f"Not found (optional, v{self.version_required}+ recommended)",
                    )
                )
            else:
                return (
                    status_part + " " + colorize(colors.YELLOW, "Not found (optional)")
                )
        else:
            status_part = colorize(colors.BOLD_RED, "[nvFuser] ✗ ") + name_padded
            if self.version_required:
                return (
                    status_part
                    + " "
                    + colorize(
                        colors.BOLD_RED,
                        f"Not found (requires {self.version_required}+)",
                    )
                )
            else:
                return status_part + " " + colorize(colors.BOLD_RED, "Not found")

    def _format_incompatible(self, colors) -> str:
        """Format incompatible: [nvFuser] ✗ Python        3.7.0 < 3.8"""
        # Add asterisk for optional requirements
        name_with_marker = f"{self.name}*" if self.optional else self.name
        name_padded = f"{name_with_marker:<15}"  # Left-align with 15 char width
        status_part = colorize(colors.BOLD_RED, "[nvFuser] ✗ ") + name_padded

        if self.version_found and self.version_required:
            return (
                status_part
                + " "
                + colorize(
                    colors.BOLD_RED, f"{self.version_found} < {self.version_required}"
                )
            )
        else:
            return status_part + " " + colorize(colors.BOLD_RED, "incompatible")


                    )
                )
            else:
                return (
                    status_part + " " + colorize(colors.YELLOW, "Not found (optional)")
                )
        else:
            status_part = colorize(colors.BOLD_RED, "[nvFuser] ✗ ") + name_padded
            if self.version_required:
                return (
                    status_part
                    + " "
                    + colorize(
                        colors.BOLD_RED,
                        f"Not found (requires {self.version_required}+)",
                    )
                )
            else:
                return status_part + " " + colorize(colors.BOLD_RED, "Not found")

    def _format_incompatible(self, colors) -> str:
        """Format incompatible: [nvFuser] ✗ Python        3.7.0 < 3.8"""
        # Add asterisk for optional requirements
        name_with_marker = f"{self.name}*" if self.optional else self.name
        name_padded = f"{name_with_marker:<15}"  # Left-align with 15 char width
        status_part = colorize(colors.BOLD_RED, "[nvFuser] ✗ ") + name_padded

        if self.version_found and self.version_required:
            return (
                status_part
                + " "
                + colorize(
                    colors.BOLD_RED, f"{self.version_found} < {self.version_required}"
                )
            )
        else:
            return status_part + " " + colorize(colors.BOLD_RED, "incompatible")

class BooleanRequirement(Requirement):
    """
    Base class for requirements without version checking.

    Simple pass/fail validation (Git submodules, Ninja).
    """

    def format_status_line(self, colors) -> str:
        """Format status line without version information."""
        # Add asterisk for optional requirements
        name_with_marker = f"{self.name}*" if self.optional else self.name
        name_padded = f"{name_with_marker:<15}"  # Left-align with 15 char width

        if self.status == RequirementStatus.SUCCESS:
            status_part = colorize(colors.GREEN, "[nvFuser] ✓ ") + name_padded
            if self.location:
                return status_part + " " + colorize(colors.CYAN, f"({self.location})")
            return status_part


# SPDX-FileCopyrightText: Copyright (c) 2025-present NVIDIA CORPORATION & AFFILIATES.
# All rights reserved.
# SPDX-License-Identifier: BSD-3-Clause
"""pybind11 dependency requirement."""

from typing import Dict
from .base import VersionRequirement

class Pybind11Requirement(VersionRequirement):
    """
    pybind11 requirement for Python bindings.

    CMake variables used:
    - pybind11_FOUND: Whether pybind11 was found
    - pybind11_VERSION: Detected version (e.g., "3.0.1")
    - pybind11_DIR: Path to pybind11 CMake config
    - NVFUSER_REQUIREMENT_pybind11_STATUS: Validation status
    - NVFUSER_REQUIREMENT_pybind11_VERSION_MIN: Minimum required version
    - NVFUSER_REQUIREMENT_pybind11_OPTIONAL: Whether pybind11 is optional
    """

    def __init__(self, cmake_vars: Dict):
        """
        Initialize pybind11 requirement.

        Args:
            cmake_vars: Dictionary of all CMake variables
        """
        # Define dependency name and CMake variable names for this requirement
        name = "pybind11"
        found_var = "pybind11_FOUND"
        status_var = "NVFUSER_REQUIREMENT_pybind11_STATUS"
        optional_var = "NVFUSER_REQUIREMENT_pybind11_OPTIONAL"
        version_found_var = "pybind11_VERSION"
        version_required_var = "NVFUSER_REQUIREMENT_pybind11_VERSION_MIN"
        location_var = "pybind11_DIR"

        super().__init__(
            name,
            cmake_vars,
            found_var,
            status_var,
            optional_var,
            version_found_var,
            version_required_var,
            location_var,
        )

    def generate_help(self, platform_info):
        """
        Generate pybind11 installation help.

        Args:
            platform_info: Platform detection dict from detect_platform()
        """
        version_min = self.version_required

        print(f"pybind11 {version_min}+ Required")
        print()
        print("Why: pybind11 provides Python bindings for nvFuser's C++ code.")
        print()
        print(f"Install pybind11 {version_min} or higher:")
        print()
        print(f"  pip install 'pybind11[global]>={version_min}'")
        print()
        print("  Note: The [global] extra provides CMake integration.")
        print()


# SPDX-License-Identifier: BSD-3-Clause
"""pybind11 dependency requirement."""

from typing import Dict
from .base import VersionRequirement

class Pybind11Requirement(VersionRequirement):
    """
    pybind11 requirement for Python bindings.

    CMake variables used:
    - pybind11_FOUND: Whether pybind11 was found
    - pybind11_VERSION: Detected version (e.g., "3.0.1")
    - pybind11_DIR: Path to pybind11 CMake config
    - NVFUSER_REQUIREMENT_pybind11_STATUS: Validation status
    - NVFUSER_REQUIREMENT_pybind11_VERSION_MIN: Minimum required version
    - NVFUSER_REQUIREMENT_pybind11_OPTIONAL: Whether pybind11 is optional
    """

    def __init__(self, cmake_vars: Dict):
        """
        Initialize pybind11 requirement.

        Args:
            cmake_vars: Dictionary of all CMake variables
        """
        # Define dependency name and CMake variable names for this requirement
        name = "pybind11"
        found_var = "pybind11_FOUND"
        status_var = "NVFUSER_REQUIREMENT_pybind11_STATUS"
        optional_var = "NVFUSER_REQUIREMENT_pybind11_OPTIONAL"
        version_found_var = "pybind11_VERSION"
        version_required_var = "NVFUSER_REQUIREMENT_pybind11_VERSION_MIN"
        location_var = "pybind11_DIR"

        super().__init__(
            name,
            cmake_vars,
            found_var,
            status_var,
            optional_var,
            version_found_var,
            version_required_var,
            location_var,
        )

    def generate_help(self, platform_info):
        """
        Generate pybind11 installation help.

        Args:
            platform_info: Platform detection dict from detect_platform()
        """
        version_min = self.version_required

        print(f"pybind11 {version_min}+ Required")
        print()
        print("Why: pybind11 provides Python bindings for nvFuser's C++ code.")
        print()
        print(f"Install pybind11 {version_min} or higher:")
        print()
        print(f"  pip install 'pybind11[global]>={version_min}'")
        print()
        print("  Note: The [global] extra provides CMake integration.")
        print()


        # Define dependency name and CMake variable names for this requirement
        name = "pybind11"
        found_var = "pybind11_FOUND"
        status_var = "NVFUSER_REQUIREMENT_pybind11_STATUS"
        optional_var = "NVFUSER_REQUIREMENT_pybind11_OPTIONAL"
        version_found_var = "pybind11_VERSION"
        version_required_var = "NVFUSER_REQUIREMENT_pybind11_VERSION_MIN"
        location_var = "pybind11_DIR"

        super().__init__(
            name,
            cmake_vars,
            found_var,
            status_var,
            optional_var,
            version_found_var,
            version_required_var,
            location_var,
        )

    def generate_help(self, platform_info):
        """
        Generate pybind11 installation help.

        Args:
            platform_info: Platform detection dict from detect_platform()
        """
        version_min = self.version_required

        print(f"pybind11 {version_min}+ Required")
        print()
        print("Why: pybind11 provides Python bindings for nvFuser's C++ code.")
        print()
        print(f"Install pybind11 {version_min} or higher:")
        print()
        print(f"  pip install 'pybind11[global]>={version_min}'")
        print()
        print("  Note: The [global] extra provides CMake integration.")
        print()


# SPDX-FileCopyrightText: Copyright (c) 2025-present NVIDIA CORPORATION & AFFILIATES.
# All rights reserved.
# SPDX-License-Identifier: BSD-3-Clause
"""CUDA Toolkit dependency requirement."""

from typing import Dict
from .base import VersionRequirement

class CUDAToolkitRequirement(VersionRequirement):
    """
    NVIDIA CUDA Toolkit requirement.

    CMake variables used:
    - CUDAToolkit_FOUND: Whether CUDA was found
    - CUDAToolkit_VERSION: Detected version (e.g., "13.1.80")
    - CUDAToolkit_ROOT: Path to CUDA installation
    - NVFUSER_REQUIREMENT_CUDAToolkit_STATUS: Validation status
    - NVFUSER_REQUIREMENT_CUDAToolkit_VERSION_MIN: Minimum required version
    - NVFUSER_REQUIREMENT_CUDAToolkit_OPTIONAL: Whether CUDA is optional
    """

    def __init__(self, cmake_vars: Dict):
        """
        Initialize CUDAToolkit requirement.

        Args:
            cmake_vars: Dictionary of all CMake variables
        """
        # Define dependency name and CMake variable names for this requirement
        name = "CUDAToolkit"
        found_var = "CUDAToolkit_FOUND"
        status_var = "NVFUSER_REQUIREMENT_CUDAToolkit_STATUS"
        optional_var = "NVFUSER_REQUIREMENT_CUDAToolkit_OPTIONAL"
        version_found_var = "CUDAToolkit_VERSION"
        version_required_var = "NVFUSER_REQUIREMENT_CUDAToolkit_VERSION_MIN"
        location_var = "CUDAToolkit_ROOT"

        super().__init__(
            name,
            cmake_vars,
            found_var,
            status_var,
            optional_var,
            version_found_var,
            version_required_var,
            location_var,
        )

    def generate_help(self, platform_info):
        """
        Generate CUDA Toolkit installation help.

        Args:
            platform_info: Platform detection dict from detect_platform()
        """
        version_min = self.version_required

        print(f"CUDA Toolkit {version_min}+ Required")
        print()
        print("Why: nvFuser needs the CUDA compiler (nvcc) for GPU kernel generation.")
        print()
        print(f"Install CUDA Toolkit {version_min} or higher:")
        print()
        print("  Download from NVIDIA:")
        print()
        print("    https://developer.nvidia.com/cuda-downloads")
        print()
        print("  After installation, ensure CUDA is in your PATH:")
        print()
        print("    export PATH=/usr/local/cuda/bin:$PATH")
        print("    export LD_LIBRARY_PATH=/usr/local/cuda/lib64:$LD_LIBRARY_PATH")
        print()


# SPDX-License-Identifier: BSD-3-Clause
"""CUDA Toolkit dependency requirement."""

from typing import Dict
from .base import VersionRequirement

class CUDAToolkitRequirement(VersionRequirement):
    """
    NVIDIA CUDA Toolkit requirement.

    CMake variables used:
    - CUDAToolkit_FOUND: Whether CUDA was found
    - CUDAToolkit_VERSION: Detected version (e.g., "13.1.80")
    - CUDAToolkit_ROOT: Path to CUDA installation
    - NVFUSER_REQUIREMENT_CUDAToolkit_STATUS: Validation status
    - NVFUSER_REQUIREMENT_CUDAToolkit_VERSION_MIN: Minimum required version
    - NVFUSER_REQUIREMENT_CUDAToolkit_OPTIONAL: Whether CUDA is optional
    """

    def __init__(self, cmake_vars: Dict):
        """
        Initialize CUDAToolkit requirement.

        Args:
            cmake_vars: Dictionary of all CMake variables
        """
        # Define dependency name and CMake variable names for this requirement
        name = "CUDAToolkit"
        found_var = "CUDAToolkit_FOUND"
        status_var = "NVFUSER_REQUIREMENT_CUDAToolkit_STATUS"
        optional_var = "NVFUSER_REQUIREMENT_CUDAToolkit_OPTIONAL"
        version_found_var = "CUDAToolkit_VERSION"
        version_required_var = "NVFUSER_REQUIREMENT_CUDAToolkit_VERSION_MIN"
        location_var = "CUDAToolkit_ROOT"

        super().__init__(
            name,
            cmake_vars,
            found_var,
            status_var,
            optional_var,
            version_found_var,
            version_required_var,
            location_var,
        )

    def generate_help(self, platform_info):
        """
        Generate CUDA Toolkit installation help.

        Args:
            platform_info: Platform detection dict from detect_platform()
        """
        version_min = self.version_required

        print(f"CUDA Toolkit {version_min}+ Required")
        print()
        print("Why: nvFuser needs the CUDA compiler (nvcc) for GPU kernel generation.")
        print()
        print(f"Install CUDA Toolkit {version_min} or higher:")
        print()
        print("  Download from NVIDIA:")
        print()
        print("    https://developer.nvidia.com/cuda-downloads")
        print()


        # Define dependency name and CMake variable names for this requirement
        name = "CUDAToolkit"
        found_var = "CUDAToolkit_FOUND"
        status_var = "NVFUSER_REQUIREMENT_CUDAToolkit_STATUS"
        optional_var = "NVFUSER_REQUIREMENT_CUDAToolkit_OPTIONAL"
        version_found_var = "CUDAToolkit_VERSION"
        version_required_var = "NVFUSER_REQUIREMENT_CUDAToolkit_VERSION_MIN"
        location_var = "CUDAToolkit_ROOT"

        super().__init__(
            name,
            cmake_vars,
            found_var,
            status_var,
            optional_var,
            version_found_var,
            version_required_var,
            location_var,
        )

    def generate_help(self, platform_info):
        """
        Generate CUDA Toolkit installation help.

        Args:
            platform_info: Platform detection dict from detect_platform()
        """
        version_min = self.version_required

        print(f"CUDA Toolkit {version_min}+ Required")
        print()
        print("Why: nvFuser needs the CUDA compiler (nvcc) for GPU kernel generation.")
        print()
        print(f"Install CUDA Toolkit {version_min} or higher:")
        print()
        print("  Download from NVIDIA:")
        print()
        print("    https://developer.nvidia.com/cuda-downloads")
        print()
        print("  After installation, ensure CUDA is in your PATH:")
        print()
        print("    export PATH=/usr/local/cuda/bin:$PATH")
        print("    export LD_LIBRARY_PATH=/usr/local/cuda/lib64:$LD_LIBRARY_PATH")
        print()


python.tools.prereqs.requirements.llvm

# SPDX-FileCopyrightText: Copyright (c) 2025-present NVIDIA CORPORATION & AFFILIATES.
# All rights reserved.
# SPDX-License-Identifier: BSD-3-Clause
"""LLVM dependency requirement."""

from typing import Dict
from .base import VersionRequirement

class LLVMRequirement(VersionRequirement):
    """
    LLVM requirement for Host IR JIT compilation.

    CMake variables used:
    - LLVM_FOUND: Whether LLVM was found
    - LLVM_VERSION: Detected version (e.g., "18.1.3")
    - LLVM_DIR: Path to LLVM CMake config
    - NVFUSER_REQUIREMENT_LLVM_STATUS: Validation status
    - NVFUSER_REQUIREMENT_LLVM_VERSION_MIN: Minimum required version
    - NVFUSER_REQUIREMENT_LLVM_OPTIONAL: Whether LLVM is optional
    """

    def __init__(self, cmake_vars: Dict):
        """
        Initialize LLVM requirement.

        Args:
            cmake_vars: Dictionary of all CMake variables
        """
        # Define dependency name and CMake variable names for this requirement
        name = "LLVM"
        found_var = "LLVM_FOUND"
        status_var = "NVFUSER_REQUIREMENT_LLVM_STATUS"
        optional_var = "NVFUSER_REQUIREMENT_LLVM_OPTIONAL"
        version_found_var = "LLVM_VERSION"
        version_required_var = "NVFUSER_REQUIREMENT_LLVM_VERSION_MIN"
        location_var = "LLVM_DIR"

        super().__init__(
            name,
            cmake_vars,
            found_var,
            status_var,
            optional_var,
            version_found_var,
            version_required_var,
            location_var,
        )

    def generate_help(self, platform_info):
        """
        Generate LLVM installation help.

        Args:
            platform_info: Platform detection dict from detect_platform()
        """
        import re

        version_min = self.version_required

        # Parse version to recommend a specific patch version
        try:
            clean = re.match(r"^[\d.]+", version_min.strip())
            if clean:
                parts = clean.group().rstrip(".").split(".")
                version_parts = tuple(int(p) for p in parts if p)
                if len(version_parts) >= 2:
                    recommended = f"{version_parts[0]}.{version_parts[1]}.8"
                    major_version = version_parts[0]
                else:
                    recommended = f"{version_parts[0]}.1.8"
                    major_version = version_parts[0]
            else:
                recommended = "18.1.8"
                major_version = 18
        except Exception:
            recommended = "18.1.8"
            major_version = 18

        print(f"LLVM {version_min}+ Required")
        print()
        print("Why: nvFuser uses LLVM for runtime Host IR JIT compilation.")
        print()
        print(f"Install LLVM {recommended} (recommended):")
        print()

        print("  Option 1: Prebuilt binaries (recommended, no sudo needed):")
        print()
        print(
            f"    wget https://github.com/llvm/llvm-project/releases/download/llvmorg-{recommended}/clang+llvm-{recommended}-x86_64-linux-gnu-ubuntu-18.04.tar.xz"
        )
        print(f"    tar -xf clang+llvm-{recommended}-*.tar.xz")
        print(f"    mv clang+llvm-{recommended}-* ~/.llvm/{recommended}")
        print()
        print("    # Add to PATH:")
        print(f"    export PATH=$HOME/.llvm/{recommended}/bin:$PATH")
        print()

        print("  Option 2: System package manager:")
        print()

        os_type = platform_info["os"]

        if os_type == "Linux":
            if platform_info.get("ubuntu_based"):
                print("    # Ubuntu/Debian (LLVM APT repository):")
                print("    wget https://apt.llvm.org/llvm.sh")
                print("    chmod +x llvm.sh")
                print(f"    sudo ./llvm.sh {major_version}")
                print()
            else:
                print("    # Check your distribution's package manager")
                print()
        elif os_type == "Darwin":
            print(f"    brew install llvm@{major_version}")
            print()
            print("    # Add to PATH:")
            print(f"    export PATH=/opt/homebrew/opt/llvm@{major_version}/bin:$PATH")
            print()


# SPDX-License-Identifier: BSD-3-Clause
"""LLVM dependency requirement."""

from typing import Dict
from .base import VersionRequirement

class LLVMRequirement(VersionRequirement):
    """
    LLVM requirement for Host IR JIT compilation.

    CMake variables used:
    - LLVM_FOUND: Whether LLVM was found
    - LLVM_VERSION: Detected version (e.g., "18.1.3")
    - LLVM_DIR: Path to LLVM CMake config
    - NVFUSER_REQUIREMENT_LLVM_STATUS: Validation status
    - NVFUSER_REQUIREMENT_LLVM_VERSION_MIN: Minimum required version
    - NVFUSER_REQUIREMENT_LLVM_OPTIONAL: Whether LLVM is optional
    """

    def __init__(self, cmake_vars: Dict):
        """
        Initialize LLVM requirement.

        Args:
            cmake_vars: Dictionary of all CMake variables
        """
        # Define dependency name and CMake variable names for this requirement
        name = "LLVM"
        found_var = "LLVM_FOUND"
        status_var = "NVFUSER_REQUIREMENT_LLVM_STATUS"
        optional_var = "NVFUSER_REQUIREMENT_LLVM_OPTIONAL"
        version_found_var = "LLVM_VERSION"
        version_required_var = "NVFUSER_REQUIREMENT_LLVM_VERSION_MIN"
        location_var = "LLVM_DIR"

        super().__init__(
            name,
            cmake_vars,
            found_var,
            status_var,
            optional_var,
            version_found_var,
            version_required_var,
            location_var,
        )

    def generate_help(self, platform_info):
        """
        Generate LLVM installation help.

        Args:
            platform_info: Platform detection dict from detect_platform()
        """
        import re

        version_min = self.version_required

        # Parse version to recommend a specific patch version
        try:
            clean = re.match(r"^[\d.]+", version_min.strip())
            if clean:
                parts = clean.group().rstrip(".").split(".")
                version_parts = tuple(int(p) for p in parts if p)
                if len(version_parts) >= 2:
                    recommended = f"{version_parts[0]}.{version_parts[1]}.8"


        # Define dependency name and CMake variable names for this requirement
        name = "LLVM"
        found_var = "LLVM_FOUND"
        status_var = "NVFUSER_REQUIREMENT_LLVM_STATUS"
        optional_var = "NVFUSER_REQUIREMENT_LLVM_OPTIONAL"
        version_found_var = "LLVM_VERSION"
        version_required_var = "NVFUSER_REQUIREMENT_LLVM_VERSION_MIN"
        location_var = "LLVM_DIR"

        super().__init__(
            name,
            cmake_vars,
            found_var,
            status_var,
            optional_var,
            version_found_var,
            version_required_var,
            location_var,
        )

    def generate_help(self, platform_info):
        """
        Generate LLVM installation help.

        Args:
            platform_info: Platform detection dict from detect_platform()
        """
        import re

        version_min = self.version_required

        # Parse version to recommend a specific patch version
        try:
            clean = re.match(r"^[\d.]+", version_min.strip())
            if clean:
                parts = clean.group().rstrip(".").split(".")
                version_parts = tuple(int(p) for p in parts if p)
                if len(version_parts) >= 2:
                    recommended = f"{version_parts[0]}.{version_parts[1]}.8"
                    major_version = version_parts[0]
                else:
                    recommended = f"{version_parts[0]}.1.8"
                    major_version = version_parts[0]
            else:
                recommended = "18.1.8"
                major_version = 18
        except Exception:
            recommended = "18.1.8"
            major_version = 18

        print(f"LLVM {version_min}+ Required")
        print()
        print("Why: nvFuser uses LLVM for runtime Host IR JIT compilation.")
        print()
        print(f"Install LLVM {recommended} (recommended):")
        print()

        print("  Option 1: Prebuilt binaries (recommended, no sudo needed):")
        print()
        print(
            f"    wget https://github.com/llvm/llvm-project/releases/download/llvmorg-{recommended}/clang+llvm-{recommended}-x86_64-linux-gnu-ubuntu-18.04.tar.xz"
        )
        print(f"    tar -xf clang+llvm-{recommended}-*.tar.xz")
        print(f"    mv clang+llvm-{recommended}-* ~/.llvm/{recommended}")
        print()
        print("    # Add to PATH:")
        print(f"    export PATH=$HOME/.llvm/{recommended}/bin:$PATH")
        print()

        print("  Option 2: System package manager:")
        print()

        os_type = platform_info["os"]

        if os_type == "Linux":
            if platform_info.get("ubuntu_based"):
                print("    # Ubuntu/Debian (LLVM APT repository):")
                print("    wget https://apt.llvm.org/llvm.sh")
                print("    chmod +x llvm.sh")
                print(f"    sudo ./llvm.sh {major_version}")
                print()
            else:
                print("    # Check your distribution's package manager")
                print()
        elif os_type == "Darwin":
            print(f"    brew install llvm@{major_version}")
            print()
            print("    # Add to PATH:")
            print(f"    export PATH=/opt/homebrew/opt/llvm@{major_version}/bin:$PATH")
            print()


# SPDX-FileCopyrightText: Copyright (c) 2025-present NVIDIA CORPORATION & AFFILIATES.
# All rights reserved.
# SPDX-License-Identifier: BSD-3-Clause
"""Compiler dependency requirement (GNU/Clang)."""

from typing import Dict
from .base import VersionRequirement

class CompilerRequirement(VersionRequirement):
    """
    C++ compiler requirement with name mapping.

    CMake variables used:
    - CMAKE_CXX_COMPILER_ID: Compiler name (GNU or Clang)
    - Compiler_FOUND: Whether compiler is available (always TRUE)
    - CMAKE_CXX_COMPILER_VERSION: Detected compiler version
    - CMAKE_CXX_COMPILER: Path to compiler executable
    - NVFUSER_REQUIREMENT_Compiler_STATUS: Validation status
    - NVFUSER_REQUIREMENT_Compiler_VERSION_MIN: Minimum required version (set based on compiler ID)
    - NVFUSER_REQUIREMENT_Compiler_OPTIONAL: Whether compiler is optional
    - NVFUSER_REQUIREMENT_GNU_VERSION_MIN: Minimum GNU version (from DependencyRequirements.cmake)
    - NVFUSER_REQUIREMENT_Clang_VERSION_MIN: Minimum Clang version (from DependencyRequirements.cmake)

    Note: CMake exports "GNU" or "Clang" as the name, but variables are prefixed with "Compiler_"

    """

    def __init__(self, cmake_vars: Dict):
        """
        Initialize compiler requirement.

        Args:
            cmake_vars: Dictionary of all CMake variables
        """
        # Extract compiler name from CMake (GNU or Clang)
        name = cmake_vars.get("CMAKE_CXX_COMPILER_ID", "Unknown")

        # Compiler uses "Compiler_" prefix for all variables, regardless of actual name
        found_var = "Compiler_FOUND"
        status_var = "NVFUSER_REQUIREMENT_Compiler_STATUS"
        optional_var = "NVFUSER_REQUIREMENT_Compiler_OPTIONAL"
        version_found_var = "CMAKE_CXX_COMPILER_VERSION"
        version_required_var = "NVFUSER_REQUIREMENT_Compiler_VERSION_MIN"
        location_var = "CMAKE_CXX_COMPILER"

        self.gnu_min_version = cmake_vars.get("NVFUSER_REQUIREMENT_GNU_VERSION_MIN")
        self.clang_min_version = cmake_vars.get("NVFUSER_REQUIREMENT_Clang_VERSION_MIN")

        super().__init__(
            name,
            cmake_vars,
            found_var,
            status_var,
            optional_var,
            version_found_var,
            version_required_var,
            location_var,
        )

    def generate_help(self, platform_info):
        """
        Generate compiler installation help.

        Args:
            platform_info: Platform detection dict from detect_platform()
        """
        # Use the version requirement that was set during initialization
        version_min = self.version_required

        print(f"{self.name} {version_min}+ Required")
        print()
        print("Why: nvFuser requires a modern C++ compiler with C++20 support,")
        print("     including the <format> header.")
        print()
        print(f"Install {self.name} {version_min} or higher:")
        print()

        os_type = platform_info["os"]

        if self.name == "GNU":
            if os_type == "Linux":
                if platform_info.get("ubuntu_based"):
                    print("  Option 1: Ubuntu PPA (recommended):")
                    print()
                    print("    sudo add-apt-repository ppa:ubuntu-toolchain-r/test")
                    print("    sudo apt update")
                    print(f"    sudo apt install gcc-{version_min} g++-{version_min}")
                    print()
                    print("    # Set as default:")
                    print(
                        f"    sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-{version_min} 100"
                    )
                    print(
                        f"    sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-{version_min} 100"
                    )
                    print()
                else:
                    print("  Option 1: System package manager:")
                    print()
                    print("    # Example for RHEL/CentOS:")
                    print(f"    # sudo yum install gcc-toolset-{version_min}")
                    print()

            elif os_type == "Darwin":
                print("  On macOS, use Clang instead:")
                print()
                print("    # Xcode Command Line Tools (includes Clang):")
                print("    xcode-select --install")
                print()

        elif self.name == "Clang":
            if os_type == "Linux":
                if platform_info.get("ubuntu_based"):
                    print("  Option 1: LLVM APT repository:")
                    print()
                    print("    wget https://apt.llvm.org/llvm.sh")
                    print("    chmod +x llvm.sh")
                    print(f"    sudo ./llvm.sh {version_min}")
                    print()
                else:
                    print("  Option 1: System package manager:")
                    print()
                    print(f"    # Check your distribution for clang-{version_min}")
                    print()

            elif os_type == "Darwin":
                print("  Option 1: Xcode Command Line Tools:")
                print()
                print("    xcode-select --install")
                print()

        print("  Option 2: Build from source:")
        print()
        print("    # See compiler documentation for build instructions")
        print()


class CompilerRequirement(VersionRequirement):
    """
    C++ compiler requirement with name mapping.

    CMake variables used:
    - CMAKE_CXX_COMPILER_ID: Compiler name (GNU or Clang)
    - Compiler_FOUND: Whether compiler is available (always TRUE)
    - CMAKE_CXX_COMPILER_VERSION: Detected compiler version
    - CMAKE_CXX_COMPILER: Path to compiler executable
    - NVFUSER_REQUIREMENT_Compiler_STATUS: Validation status
    - NVFUSER_REQUIREMENT_Compiler_VERSION_MIN: Minimum required version (set based on compiler ID)
    - NVFUSER_REQUIREMENT_Compiler_OPTIONAL: Whether compiler is optional
    - NVFUSER_REQUIREMENT_GNU_VERSION_MIN: Minimum GNU version (from DependencyRequirements.cmake)
    - NVFUSER_REQUIREMENT_Clang_VERSION_MIN: Minimum Clang version (from DependencyRequirements.cmake)

    Note: CMake exports "GNU" or "Clang" as the name, but variables are prefixed with "Compiler_"

    """

    def __init__(self, cmake_vars: Dict):
        """
        Initialize compiler requirement.

        Args:
            cmake_vars: Dictionary of all CMake variables
        """
        # Extract compiler name from CMake (GNU or Clang)
        name = cmake_vars.get("CMAKE_CXX_COMPILER_ID", "Unknown")

        # Compiler uses "Compiler_" prefix for all variables, regardless of actual name
        found_var = "Compiler_FOUND"
        status_var = "NVFUSER_REQUIREMENT_Compiler_STATUS"
        optional_var = "NVFUSER_REQUIREMENT_Compiler_OPTIONAL"
        version_found_var = "CMAKE_CXX_COMPILER_VERSION"
        version_required_var = "NVFUSER_REQUIREMENT_Compiler_VERSION_MIN"
        location_var = "CMAKE_CXX_COMPILER"

        self.gnu_min_version = cmake_vars.get("NVFUSER_REQUIREMENT_GNU_VERSION_MIN")
        self.clang_min_version = cmake_vars.get("NVFUSER_REQUIREMENT_Clang_VERSION_MIN")

        super().__init__(
            name,
            cmake_vars,
            found_var,
            status_var,
            optional_var,
            version_found_var,
            version_required_var,
            location_var,
        )

    def generate_help(self, platform_info):
        """
        Generate compiler installation help.

        Args:
            platform_info: Platform detection dict from detect_platform()
        """
        # Use the version requirement that was set during initialization
        version_min = self.version_required

        print(f"{self.name} {version_min}+ Required")
        print()
        print("Why: nvFuser requires a modern C++ compiler with C++20 support,")
        print("     including the <format> header.")
        print()
        print(f"Install {self.name} {version_min} or higher:")
        print()

        os_type = platform_info["os"]


        status_var = "NVFUSER_REQUIREMENT_Compiler_STATUS"
        optional_var = "NVFUSER_REQUIREMENT_Compiler_OPTIONAL"
        version_found_var = "CMAKE_CXX_COMPILER_VERSION"
        version_required_var = "NVFUSER_REQUIREMENT_Compiler_VERSION_MIN"
        location_var = "CMAKE_CXX_COMPILER"

        self.gnu_min_version = cmake_vars.get("NVFUSER_REQUIREMENT_GNU_VERSION_MIN")
        self.clang_min_version = cmake_vars.get("NVFUSER_REQUIREMENT_Clang_VERSION_MIN")

        super().__init__(
            name,
            cmake_vars,
            found_var,
            status_var,
            optional_var,
            version_found_var,
            version_required_var,
            location_var,
        )

    def generate_help(self, platform_info):
        """
        Generate compiler installation help.

        Args:
            platform_info: Platform detection dict from detect_platform()
        """
        # Use the version requirement that was set during initialization
        version_min = self.version_required

        print(f"{self.name} {version_min}+ Required")
        print()
        print("Why: nvFuser requires a modern C++ compiler with C++20 support,")
        print("     including the <format> header.")
        print()
        print(f"Install {self.name} {version_min} or higher:")
        print()

        os_type = platform_info["os"]

        if self.name == "GNU":
            if os_type == "Linux":
                if platform_info.get("ubuntu_based"):
                    print("  Option 1: Ubuntu PPA (recommended):")
                    print()
                    print("    sudo add-apt-repository ppa:ubuntu-toolchain-r/test")
                    print("    sudo apt update")
                    print(f"    sudo apt install gcc-{version_min} g++-{version_min}")
                    print()
                    print("    # Set as default:")
                    print(
                        f"    sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-{version_min} 100"
                    )
                    print(
                        f"    sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-{version_min} 100"
                    )
                    print()
                else:
                    print("  Option 1: System package manager:")
                    print()
                    print("    # Example for RHEL/CentOS:")
                    print(f"    # sudo yum install gcc-toolset-{version_min}")
                    print()

            elif os_type == "Darwin":
                print("  On macOS, use Clang instead:")
                print()
                print("    # Xcode Command Line Tools (includes Clang):")
                print("    xcode-select --install")
                print()

        elif self.name == "Clang":
            if os_type == "Linux":
                if platform_info.get("ubuntu_based"):
                    print("  Option 1: LLVM APT repository:")
                    print()
                    print("    wget https://apt.llvm.org/llvm.sh")
                    print("    chmod +x llvm.sh")
                    print(f"    sudo ./llvm.sh {version_min}")
                    print()
                else:
                    print("  Option 1: System package manager:")
                    print()
                    print(f"    # Check your distribution for clang-{version_min}")
                    print()

            elif os_type == "Darwin":
                print("  Option 1: Xcode Command Line Tools:")
                print()
                print("    xcode-select --install")
                print()

        print("  Option 2: Build from source:")
        print()
        print("    # See compiler documentation for build instructions")
        print()


python.tools.prereqs.requirements.ninja

# SPDX-FileCopyrightText: Copyright (c) 2025-present NVIDIA CORPORATION & AFFILIATES.
# All rights reserved.
# SPDX-License-Identifier: BSD-3-Clause
"""Ninja build system dependency requirement."""

from typing import Dict
from .base import BooleanRequirement

class NinjaRequirement(BooleanRequirement):
    """
    Ninja build system check.

    CMake variables used:
    - Ninja_FOUND: Whether Ninja is available
    - NVFUSER_REQUIREMENT_Ninja_STATUS: Validation status
    - NVFUSER_REQUIREMENT_Ninja_OPTIONAL: Whether Ninja is optional

    No version checking - just verifies Ninja is available.
    """

    def __init__(self, cmake_vars: Dict):
        """
        Initialize Ninja requirement.

        Args:
            cmake_vars: Dictionary of all CMake variables
        """
        # Define dependency name and CMake variable names for this requirement
        name = "Ninja"
        found_var = "Ninja_FOUND"
        status_var = "NVFUSER_REQUIREMENT_Ninja_STATUS"
        optional_var = "NVFUSER_REQUIREMENT_Ninja_OPTIONAL"
        location_var = ""

        super().__init__(
            name, cmake_vars, found_var, status_var, optional_var, location_var
        )

    def generate_help(self, platform_info):
        """
        Generate Ninja installation help.

        Args:
            platform_info: Platform detection dict from detect_platform()
        """
        print("Ninja Build System")
        print()
        print(
            "Why: Ninja is a fast build system used by nvFuser for faster compilation."
        )
        print()
        print("Install Ninja:")
        print()

        os_type = platform_info["os"]

        if os_type == "Linux":
            if platform_info.get("ubuntu_based"):
                print("  Option 1: Ubuntu/Debian:")
                print()
                print("    sudo apt update")
                print("    sudo apt install ninja-build")
                print()
            else:
                print("  Option 1: System package manager:")
                print()
                print("    # Example for RHEL/CentOS:")
                print("    # sudo yum install ninja-build")
                print()

        elif os_type == "Darwin":
            print("  Option 1: Homebrew:")
            print()
            print("    brew install ninja")
            print()

        print("  Option 2: pip:")
        print()
        print("    pip install ninja")
        print()
        print("  Configuring CMake")
        print()
        print("    Pass Ninja as the CMake Generator")
        print("    CMake <options> -G Ninja")
        print()


# All rights reserved.
# SPDX-License-Identifier: BSD-3-Clause
"""Ninja build system dependency requirement."""

from typing import Dict
from .base import BooleanRequirement

class NinjaRequirement(BooleanRequirement):
    """
    Ninja build system check.

    CMake variables used:
    - Ninja_FOUND: Whether Ninja is available
    - NVFUSER_REQUIREMENT_Ninja_STATUS: Validation status
    - NVFUSER_REQUIREMENT_Ninja_OPTIONAL: Whether Ninja is optional

    No version checking - just verifies Ninja is available.
    """

    def __init__(self, cmake_vars: Dict):
        """
        Initialize Ninja requirement.

        Args:
            cmake_vars: Dictionary of all CMake variables
        """
        # Define dependency name and CMake variable names for this requirement
        name = "Ninja"
        found_var = "Ninja_FOUND"
        status_var = "NVFUSER_REQUIREMENT_Ninja_STATUS"
        optional_var = "NVFUSER_REQUIREMENT_Ninja_OPTIONAL"
        location_var = ""

        super().__init__(
            name, cmake_vars, found_var, status_var, optional_var, location_var
        )

    def generate_help(self, platform_info):
        """
        Generate Ninja installation help.

        Args:
            platform_info: Platform detection dict from detect_platform()
        """
        print("Ninja Build System")
        print()
        print(
            "Why: Ninja is a fast build system used by nvFuser for faster compilation."
        )
        print()
        print("Install Ninja:")
        print()

        os_type = platform_info["os"]

        if os_type == "Linux":


    """

    def __init__(self, cmake_vars: Dict):
        """
        Initialize Ninja requirement.

        Args:
            cmake_vars: Dictionary of all CMake variables
        """
        # Define dependency name and CMake variable names for this requirement
        name = "Ninja"
        found_var = "Ninja_FOUND"
        status_var = "NVFUSER_REQUIREMENT_Ninja_STATUS"
        optional_var = "NVFUSER_REQUIREMENT_Ninja_OPTIONAL"
        location_var = ""

        super().__init__(
            name, cmake_vars, found_var, status_var, optional_var, location_var
        )

    def generate_help(self, platform_info):
        """
        Generate Ninja installation help.

        Args:
            platform_info: Platform detection dict from detect_platform()
        """
        print("Ninja Build System")
        print()
        print(
            "Why: Ninja is a fast build system used by nvFuser for faster compilation."
        )
        print()
        print("Install Ninja:")
        print()

        os_type = platform_info["os"]

        if os_type == "Linux":
            if platform_info.get("ubuntu_based"):
                print("  Option 1: Ubuntu/Debian:")
                print()
                print("    sudo apt update")
                print("    sudo apt install ninja-build")
                print()
            else:
                print("  Option 1: System package manager:")
                print()
                print("    # Example for RHEL/CentOS:")
                print("    # sudo yum install ninja-build")
                print()

        elif os_type == "Darwin":
            print("  Option 1: Homebrew:")
            print()
            print("    brew install ninja")
            print()

        print("  Option 2: pip:")
        print()
        print("    pip install ninja")
        print()
        print("  Configuring CMake")
        print()
        print("    Pass Ninja as the CMake Generator")
        print("    CMake <options> -G Ninja")
        print()


python.tools.prereqs.requirements.torch

# SPDX-FileCopyrightText: Copyright (c) 2025-present NVIDIA CORPORATION & AFFILIATES.
# All rights reserved.
# SPDX-License-Identifier: BSD-3-Clause
"""PyTorch dependency requirement with CUDA constraint validation."""

from typing import Dict
from .base import VersionRequirement
from ..colors import colorize

class TorchRequirement(VersionRequirement):
    """
    PyTorch requirement with CUDA version constraint checking.

    CMake variables used:
    - Torch_FOUND: Whether Torch was found
    - Torch_VERSION: Detected PyTorch version
    - Torch_DIR: Path to Torch CMake config
    - NVFUSER_REQUIREMENT_Torch_STATUS: Validation status
    - NVFUSER_REQUIREMENT_Torch_VERSION_MIN: Minimum required version
    - NVFUSER_REQUIREMENT_Torch_OPTIONAL: Whether Torch is optional
    - Torch_CUDA_constraint_status: CUDA constraint validation result
        - "match": Torch CUDA == CUDAToolkit version
        - "mismatch": Versions don't match (FAILURE)
        - "not_available": Torch built without CUDA
    - Torch_CUDA_constraint_version: CUDA version if match
    - Torch_CUDA_constraint_found: Torch's CUDA version (if mismatch)
    - Torch_CUDA_constraint_required: System's CUDA Toolkit version (if mismatch)

    Special: Also validates CUDA version constraint
    """

    def __init__(self, cmake_vars: Dict):
        """
        Initialize Torch requirement.

        Args:
            cmake_vars: Dictionary of all CMake variables
        """
        # Define dependency name and CMake variable names for this requirement
        name = "Torch"
        found_var = "Torch_FOUND"
        status_var = "NVFUSER_REQUIREMENT_Torch_STATUS"
        optional_var = "NVFUSER_REQUIREMENT_Torch_OPTIONAL"
        version_found_var = "Torch_VERSION"
        version_required_var = "NVFUSER_REQUIREMENT_Torch_VERSION_MIN"
        location_var = "Torch_DIR"

        super().__init__(
            name,
            cmake_vars,
            found_var,
            status_var,
            optional_var,
            version_found_var,
            version_required_var,
            location_var,
        )

        # Extract Torch CUDA constraint variables from cmake_vars
        self.constraint_status = cmake_vars.get(f"{name}_CUDA_constraint_status")
        self.constraint_version = cmake_vars.get(f"{name}_CUDA_constraint_version")
        self.constraint_found = cmake_vars.get(f"{name}_CUDA_constraint_found")
        self.constraint_required = cmake_vars.get(f"{name}_CUDA_constraint_required")

    def format_status_line(self, colors) -> str:
        """Format with both Torch version and CUDA constraint."""
        # Main Torch version line (base class handles location)
        main_line = super().format_status_line(colors)

        # Add CUDA constraint line
        constraint_line = self._format_cuda_constraint(colors)

        if constraint_line:
            return main_line + "\n" + constraint_line
        else:
            return main_line

    def _format_cuda_constraint(self, colors) -> str:
        """Format CUDA constraint validation line."""
        if not self.constraint_status:
            return ""

        # Use same padding as main dependency name
        name_padded = f"{'Torch_CUDA':<15}"

        if self.constraint_status == "match":
            cuda_version = self.constraint_version or "unknown"
            status_part = colorize(colors.GREEN, "[nvFuser] ✓") + " " + name_padded
            # Use cyan for the CUDA version/result, matching location color
            version_part = colorize(
                colors.CYAN, f"{cuda_version} (Torch.CUDA == CUDAToolkit)"
            )
            return f"{status_part} {version_part}"
        elif self.constraint_status == "mismatch":
            torch_cuda = self.constraint_found or "unknown"
            toolkit_cuda = self.constraint_required or "unknown"
            status_part = colorize(colors.BOLD_RED, "[nvFuser] ✗") + " " + name_padded
            error_part = colorize(
                colors.BOLD_RED,
                f"mismatch (Torch: {torch_cuda}, CUDAToolkit: {toolkit_cuda})",
            )
            return f"{status_part} {error_part}"
        elif self.constraint_status == "not_available":
            status_part = colorize(colors.YELLOW, "[nvFuser] ○") + " " + name_padded
            message_part = colorize(colors.YELLOW, "Torch built without CUDA")
            return f"{status_part} {message_part}"
        else:
            return ""

    def is_failure(self) -> bool:
        """Check for both version failure and CUDA constraint failure."""
        # Check base version requirement
        if super().is_failure():
            return True

        # Check CUDA constraint
        if self.constraint_status == "mismatch":
            return True

        return False

    def generate_help(self, platform_info):
        """
        Generate PyTorch installation help.

        Args:
            platform_info: Platform detection dict from detect_platform()
        """
        version_min = self.version_required

        print(f"PyTorch {version_min}+ Required")
        print()
        print(
            "Why: nvFuser is a PyTorch extension and requires PyTorch with CUDA support."
        )
        print()
        print(f"Install PyTorch {version_min} or higher with CUDA:")
        print()

        # Show common CUDA versions
        print("  # For CUDA 13.1:")
        print("  pip install torch --index-url https://download.pytorch.org/whl/cu131")
        print("  # For CUDA 13.0:")
        print("  pip install torch --index-url https://download.pytorch.org/whl/cu130")
        print("  # For CUDA 12.8:")
        print("  pip install torch --index-url https://download.pytorch.org/whl/cu128")
        print()

        # If CUDA constraint mismatch, add additional help
        if self.constraint_status == "mismatch":
            print()
            print("IMPORTANT: Torch CUDA Version Mismatch Detected")
            print()
            print(
                "Why: PyTorch was built with a different CUDA version than your system's"
            )
            print("     CUDA Toolkit. This will cause runtime errors.")
            print()
            print("Resolution:")
            print()
            print("  You have two options:")
            print()
            print("  Option 1: Install matching CUDA Toolkit (recommended)")
            print()
            print(
                "    Install the CUDA Toolkit version that matches your PyTorch build."
            )
            print(
                "    Check PyTorch CUDA version: python -c 'import torch; print(torch.version.cuda)'"
            )
            print()
            print("  Option 2: Reinstall PyTorch for your CUDA version")
            print()
            print("    Reinstall PyTorch built for your system's CUDA Toolkit version.")
            print("    Check system CUDA version: nvcc --version")
            print()


    PyTorch requirement with CUDA version constraint checking.

    CMake variables used:
    - Torch_FOUND: Whether Torch was found
    - Torch_VERSION: Detected PyTorch version
    - Torch_DIR: Path to Torch CMake config
    - NVFUSER_REQUIREMENT_Torch_STATUS: Validation status
    - NVFUSER_REQUIREMENT_Torch_VERSION_MIN: Minimum required version
    - NVFUSER_REQUIREMENT_Torch_OPTIONAL: Whether Torch is optional
    - Torch_CUDA_constraint_status: CUDA constraint validation result
        - "match": Torch CUDA == CUDAToolkit version
        - "mismatch": Versions don't match (FAILURE)
        - "not_available": Torch built without CUDA
    - Torch_CUDA_constraint_version: CUDA version if match
    - Torch_CUDA_constraint_found: Torch's CUDA version (if mismatch)
    - Torch_CUDA_constraint_required: System's CUDA Toolkit version (if mismatch)

    Special: Also validates CUDA version constraint
    """

    def __init__(self, cmake_vars: Dict):
        """
        Initialize Torch requirement.

        Args:
            cmake_vars: Dictionary of all CMake variables
        """
        # Define dependency name and CMake variable names for this requirement
        name = "Torch"
        found_var = "Torch_FOUND"
        status_var = "NVFUSER_REQUIREMENT_Torch_STATUS"
        optional_var = "NVFUSER_REQUIREMENT_Torch_OPTIONAL"
        version_found_var = "Torch_VERSION"
        version_required_var = "NVFUSER_REQUIREMENT_Torch_VERSION_MIN"
        location_var = "Torch_DIR"

        super().__init__(
            name,
            cmake_vars,
            found_var,
            status_var,
            optional_var,
            version_found_var,
            version_required_var,
            location_var,
        )

        # Extract Torch CUDA constraint variables from cmake_vars
        self.constraint_status = cmake_vars.get(f"{name}_CUDA_constraint_status")
        self.constraint_version = cmake_vars.get(f"{name}_CUDA_constraint_version")
        self.constraint_found = cmake_vars.get(f"{name}_CUDA_constraint_found")
        self.constraint_required = cmake_vars.get(f"{name}_CUDA_constraint_required")

    def format_status_line(self, colors) -> str:
        """Format with both Torch version and CUDA constraint."""
        # Main Torch version line (base class handles location)
        main_line = super().format_status_line(colors)

        # Add CUDA constraint line
        constraint_line = self._format_cuda_constraint(colors)

        if constraint_line:
            return main_line + "\n" + constraint_line
        else:
            return main_line

    def _format_cuda_constraint(self, colors) -> str:
        """Format CUDA constraint validation line."""
        if not self.constraint_status:
            return ""

        # Use same padding as main dependency name


        version_required_var = "NVFUSER_REQUIREMENT_Torch_VERSION_MIN"
        location_var = "Torch_DIR"

        super().__init__(
            name,
            cmake_vars,
            found_var,
            status_var,
            optional_var,
            version_found_var,
            version_required_var,
            location_var,
        )

        # Extract Torch CUDA constraint variables from cmake_vars
        self.constraint_status = cmake_vars.get(f"{name}_CUDA_constraint_status")
        self.constraint_version = cmake_vars.get(f"{name}_CUDA_constraint_version")
        self.constraint_found = cmake_vars.get(f"{name}_CUDA_constraint_found")
        self.constraint_required = cmake_vars.get(f"{name}_CUDA_constraint_required")

    def format_status_line(self, colors) -> str:
        """Format with both Torch version and CUDA constraint."""
        # Main Torch version line (base class handles location)
        main_line = super().format_status_line(colors)

        # Add CUDA constraint line
        constraint_line = self._format_cuda_constraint(colors)

        if constraint_line:
            return main_line + "\n" + constraint_line
        else:
            return main_line

    def _format_cuda_constraint(self, colors) -> str:
        """Format CUDA constraint validation line."""
        if not self.constraint_status:
            return ""

        # Use same padding as main dependency name
        name_padded = f"{'Torch_CUDA':<15}"

        if self.constraint_status == "match":
            cuda_version = self.constraint_version or "unknown"
            status_part = colorize(colors.GREEN, "[nvFuser] ✓") + " " + name_padded
            # Use cyan for the CUDA version/result, matching location color
            version_part = colorize(
                colors.CYAN, f"{cuda_version} (Torch.CUDA == CUDAToolkit)"
            )
            return f"{status_part} {version_part}"
        elif self.constraint_status == "mismatch":
            torch_cuda = self.constraint_found or "unknown"
            toolkit_cuda = self.constraint_required or "unknown"


        # Extract Torch CUDA constraint variables from cmake_vars
        self.constraint_status = cmake_vars.get(f"{name}_CUDA_constraint_status")
        self.constraint_version = cmake_vars.get(f"{name}_CUDA_constraint_version")
        self.constraint_found = cmake_vars.get(f"{name}_CUDA_constraint_found")
        self.constraint_required = cmake_vars.get(f"{name}_CUDA_constraint_required")

    def format_status_line(self, colors) -> str:
        """Format with both Torch version and CUDA constraint."""
        # Main Torch version line (base class handles location)
        main_line = super().format_status_line(colors)

        # Add CUDA constraint line
        constraint_line = self._format_cuda_constraint(colors)

        if constraint_line:
            return main_line + "\n" + constraint_line
        else:
            return main_line

    def _format_cuda_constraint(self, colors) -> str:
        """Format CUDA constraint validation line."""
        if not self.constraint_status:
            return ""

        # Use same padding as main dependency name
        name_padded = f"{'Torch_CUDA':<15}"

        if self.constraint_status == "match":
            cuda_version = self.constraint_version or "unknown"
            status_part = colorize(colors.GREEN, "[nvFuser] ✓") + " " + name_padded
            # Use cyan for the CUDA version/result, matching location color
            version_part = colorize(
                colors.CYAN, f"{cuda_version} (Torch.CUDA == CUDAToolkit)"
            )
            return f"{status_part} {version_part}"
        elif self.constraint_status == "mismatch":
            torch_cuda = self.constraint_found or "unknown"
            toolkit_cuda = self.constraint_required or "unknown"
            status_part = colorize(colors.BOLD_RED, "[nvFuser] ✗") + " " + name_padded
            error_part = colorize(
                colors.BOLD_RED,
                f"mismatch (Torch: {torch_cuda}, CUDAToolkit: {toolkit_cuda})",
            )
            return f"{status_part} {error_part}"
        elif self.constraint_status == "not_available":
            status_part = colorize(colors.YELLOW, "[nvFuser] ○") + " " + name_padded
            message_part = colorize(colors.YELLOW, "Torch built without CUDA")
            return f"{status_part} {message_part}"
        else:
            return ""

    def is_failure(self) -> bool:
        """Check for both version failure and CUDA constraint failure."""
        # Check base version requirement
        if super().is_failure():
            return True

        # Check CUDA constraint
        if self.constraint_status == "mismatch":
            return True

        return False

    def generate_help(self, platform_info):
        """
        Generate PyTorch installation help.

        Args:
            platform_info: Platform detection dict from detect_platform()
        """


            version_part = colorize(
                colors.CYAN, f"{cuda_version} (Torch.CUDA == CUDAToolkit)"
            )
            return f"{status_part} {version_part}"
        elif self.constraint_status == "mismatch":
            torch_cuda = self.constraint_found or "unknown"
            toolkit_cuda = self.constraint_required or "unknown"
            status_part = colorize(colors.BOLD_RED, "[nvFuser] ✗") + " " + name_padded
            error_part = colorize(
                colors.BOLD_RED,
                f"mismatch (Torch: {torch_cuda}, CUDAToolkit: {toolkit_cuda})",
            )
            return f"{status_part} {error_part}"
        elif self.constraint_status == "not_available":
            status_part = colorize(colors.YELLOW, "[nvFuser] ○") + " " + name_padded
            message_part = colorize(colors.YELLOW, "Torch built without CUDA")
            return f"{status_part} {message_part}"
        else:
            return ""

    def is_failure(self) -> bool:
        """Check for both version failure and CUDA constraint failure."""
        # Check base version requirement
        if super().is_failure():
            return True

        # Check CUDA constraint
        if self.constraint_status == "mismatch":
            return True

        return False

    def generate_help(self, platform_info):
        """
        Generate PyTorch installation help.

        Args:
            platform_info: Platform detection dict from detect_platform()
        """
        version_min = self.version_required

        print(f"PyTorch {version_min}+ Required")
        print()
        print(
            "Why: nvFuser is a PyTorch extension and requires PyTorch with CUDA support."
        )
        print()
        print(f"Install PyTorch {version_min} or higher with CUDA:")
        print()

        # Show common CUDA versions