stop

            )

        self.profiler_output: list[tuple[str, float]] = []

        # Subscribe to CUPTI and register activity callbacks.
        CuptiProfiler.__subscriber_handle = cupti_call_safe(cupti.subscribe, None, None)
        cupti_call_safe(
            cupti.activity_register_callbacks,
            self._func_buffer_requested,
            self._func_buffer_completed,
        )
        self.is_valid = True

    def start(self) -> None:
        self._error_if_not_valid()
        cupti_call_safe(cupti.activity_flush_all, 1)
        self.profiler_output = []
        for activity_kind in CuptiProfiler.activity_kinds:
            cupti_call_safe(cupti.activity_enable, activity_kind)

    def stop(self) -> list[tuple[str, float]]:
        self._error_if_not_valid()
        for activity_kind in CuptiProfiler.activity_kinds:
            cupti_call_safe(cupti.activity_disable, activity_kind)
        cupti_call_safe(cupti.activity_flush_all, 0)
        return self.profiler_output

    def teardown_cupti(self) -> None:
        self._error_if_not_valid()
        if CuptiProfiler.__subscriber_handle is None:
            return
        cupti_call_safe(cupti.unsubscribe, CuptiProfiler.__subscriber_handle)
        cupti_call_safe(cupti.finalize)
        CuptiProfiler.__subscriber_handle = None
        # Invalidate the profiler so it cannot be used again.
        self.is_valid = False

class CuptiTimer(Timer):
    def __init__(self):
        super().__init__()
        self.cupti_profiler = CuptiProfiler()
        self.is_running = False

    def __call__(self):
        torch.cuda.synchronize()


            cupti.activity_register_callbacks,
            self._func_buffer_requested,
            self._func_buffer_completed,
        )
        self.is_valid = True

    def start(self) -> None:
        self._error_if_not_valid()
        cupti_call_safe(cupti.activity_flush_all, 1)
        self.profiler_output = []
        for activity_kind in CuptiProfiler.activity_kinds:
            cupti_call_safe(cupti.activity_enable, activity_kind)

    def stop(self) -> list[tuple[str, float]]:
        self._error_if_not_valid()
        for activity_kind in CuptiProfiler.activity_kinds:
            cupti_call_safe(cupti.activity_disable, activity_kind)
        cupti_call_safe(cupti.activity_flush_all, 0)
        return self.profiler_output

    def teardown_cupti(self) -> None:
        self._error_if_not_valid()
        if CuptiProfiler.__subscriber_handle is None:
            return
        cupti_call_safe(cupti.unsubscribe, CuptiProfiler.__subscriber_handle)
        cupti_call_safe(cupti.finalize)
        CuptiProfiler.__subscriber_handle = None
        # Invalidate the profiler so it cannot be used again.
        self.is_valid = False

class CuptiTimer(Timer):
    def __init__(self):
        super().__init__()
        self.cupti_profiler = CuptiProfiler()
        self.is_running = False

    def __call__(self):
        torch.cuda.synchronize()

        if not self.is_running:
            self.cupti_profiler.start()
            self.is_running = True
            return self.current_time

        profiler_output = self.cupti_profiler.stop()
        self.is_running = False

        # Check if any activities were recorded


        if len(profiler_output) == 0:
            self.cleanup()
            raise RuntimeError("No activities were recorded.")

        self._increment_global_time(sum(duration for _, duration in profiler_output))
        return self.current_time

    def cleanup(self):
        self.is_running = False
        self.cupti_profiler.teardown_cupti()

class FusionProfileTimer(Timer):
    def __init__(self):
        super().__init__()
        self.fd = None
        # Specifies if the timer in host measurement is called at the start/finish of execution.
        # Timings are measured at the end of execution.
        self.execution_start = True

    def set_fd(self, fd):
        self.fd = fd

    def __call__(self):
        if not self.execution_start:
            profile = get_fusion_profile()
            elapsed_host_time = profile.host_time_ms / 1e3
            self._increment_global_time(elapsed_host_time)
        self.execution_start = not self.execution_start
        return self.current_time


python.tools.memory

# SPDX-FileCopyrightText: Copyright (c) 2023-present NVIDIA CORPORATION & AFFILIATES.
# All rights reserved.
# SPDX-License-Identifier: BSD-3-Clause

def get_available_memory_gb():
    """Returns the available memory in GB."""
    try:
        import psutil

        return psutil.virtual_memory().available / 1024 / 1024 / 1024
    except:  # noqa: E722
        pass

    try:
        with open("/proc/meminfo", "r") as f:
            while True:
                line = f.readline()
                if line.startswith("MemAvailable:"):
                    mem = line.split()[1]
                    assert line.split()[2] == "kB"
                    return int(mem) / 1024 / 1024
                if not line:
                    break
    except:  # noqa: E722
        pass

    return 0


#!/usr/bin/env python3
# SPDX-FileCopyrightText: Copyright (c) 2025-present NVIDIA CORPORATION & AFFILIATES.
# All rights reserved.
# SPDX-License-Identifier: BSD-3-Clause
"""
nvFuser Dependency Report Generator

Reads dependency data from JSON (generated by CMake) and prints
a comprehensive, user-friendly report with colored output and
actionable installation instructions for missing dependencies.

IMPORTANT: CMake is the source of truth for all dependency requirements.
This script only formats output and provides help text.
"""

import json
import sys
from pathlib import Path
from typing import Dict

from prereqs import detect_platform, format_platform_info
from prereqs.colors import colorize, Colors
from prereqs.requirements import (
    PythonRequirement,
    TorchRequirement,
    LLVMRequirement,
    CUDAToolkitRequirement,
    Pybind11Requirement,
    CompilerRequirement,
    NinjaRequirement,
    NVMMHRequirement,
    GitSubmodulesRequirement,
)

class DependencyReporter:
    """
    Generates formatted dependency reports from JSON data with help text.

    CMake provides all dependency data via JSON. This class formats the output
    and generates platform-specific installation instructions for failures.
    """

    def __init__(self, deps_path: Path):
        # Load CMake variables
        cmake_vars = self._load_cmake_vars(deps_path)

        self.colors = Colors()
        self.platform_info = detect_platform()

        # Create requirement objects - each class defines its own name and variable names
        self.requirements = []
        self.requirements.append(NinjaRequirement(cmake_vars))
        self.requirements.append(GitSubmodulesRequirement(cmake_vars))
        self.requirements.append(CompilerRequirement(cmake_vars))
        self.requirements.append(PythonRequirement(cmake_vars))
        self.requirements.append(CUDAToolkitRequirement(cmake_vars))
        self.requirements.append(TorchRequirement(cmake_vars))
        self.requirements.append(Pybind11Requirement(cmake_vars))
        self.requirements.append(LLVMRequirement(cmake_vars))
        self.requirements.append(NVMMHRequirement(cmake_vars))

    def _load_cmake_vars(self, deps_path: Path) -> Dict:
        """Load CMake variables from JSON file"""
        try:
            with open(deps_path, "r") as f:
                data = json.load(f)
                return data.get("cmake_vars", {})
        except FileNotFoundError:
            print(f"Error: {deps_path} not found", file=sys.stderr)
            sys.exit(1)
        except Exception as e:
            print(f"Error loading dependencies: {e}", file=sys.stderr)
            sys.exit(1)

    def generate_report(self):
        """Main entry point - prints formatted report"""
        print()  # Blank line before report
        print("=" * 80)
        self._print_header()
        print("=" * 80)
        print()  # Blank line after header
        self._print_dependencies()
        print()  # Blank line after dependencies
        print("* Optional requirement")
        print("=" * 80)

        # Collect failures and issues to show help for
        # Include required failures (is_failure=True) and optional issues (not SUCCESS)
        failures = []
        for req in self.requirements:
            if hasattr(req, "is_failure") and req.is_failure():
                # Required dependency that failed
                failures.append(req)
            elif (
                hasattr(req, "status")
                and hasattr(req, "optional")
                and req.optional
                and req.status != "SUCCESS"
            ):
                # Optional dependency with issues (NOT_FOUND or INCOMPATIBLE)
                failures.append(req)

        if failures:
            self._print_help_section(failures)
            # Only print failure summary if there are actual (non-optional) failures
            required_failures = [
                req
                for req in self.requirements
                if hasattr(req, "is_failure") and req.is_failure()
            ]
            if required_failures:
                self._print_failure_summary()

        print()  # Blank line at end

    def _print_header(self):
        """Print report header with platform information"""
        platform_str = format_platform_info(self.platform_info)
        print(
            colorize(
                self.colors.BOLD_GREEN, "[nvFuser] Validating build prerequisites..."
            )
        )
        print(colorize(self.colors.CYAN, "Platform: ") + platform_str)

    def _print_failure_summary(self):
        """Print failure summary message"""
        print()
        print(colorize(self.colors.BOLD_RED, "Build prerequisite validation FAILED"))
        print("See installation instructions above")

    def _print_dependencies(self):
        """Print status for each dependency using OOP requirement classes"""
        for req in self.requirements:
            if hasattr(req, "format_status_line"):
                # OOP: use requirement's format method
                print(req.format_status_line(self.colors))
            else:
                # Fallback: use legacy dict-based formatting (shouldn't happen)
                print(f"[nvFuser] ? {getattr(req, 'name', 'Unknown')}")

    def _print_help_section(self, failures):
        """Print help section header and help for each failed dependency"""
        print()
        print("=" * 70)
        print("Installation Instructions")
        print("=" * 70)
        print()

        for req in failures:
            self._print_help_for_requirement(req)

    def _print_help_for_requirement(self, req):
        """Call requirement's help generation method"""
        if hasattr(req, "generate_help"):
            req.generate_help(self.platform_info)
        else:
            # Fallback for requirements without help
            print(f"{req.name} installation help not available")
            print()

def main():
    if len(sys.argv) != 2:
        print("Usage: check_dependencies.py <deps.json>", file=sys.stderr)
        sys.exit(1)

    json_path = Path(sys.argv[1])
    if not json_path.exists():
        print(f"Error: {json_path} not found", file=sys.stderr)
        sys.exit(1)

    # Generate report
    reporter = DependencyReporter(json_path)
    reporter.generate_report()

    # Report generated successfully
    sys.exit(0)

if __name__ == "__main__":
    main()


DependencyReporter

import json
import sys
from pathlib import Path
from typing import Dict

from prereqs import detect_platform, format_platform_info
from prereqs.colors import colorize, Colors
from prereqs.requirements import (
    PythonRequirement,
    TorchRequirement,
    LLVMRequirement,
    CUDAToolkitRequirement,
    Pybind11Requirement,
    CompilerRequirement,
    NinjaRequirement,
    NVMMHRequirement,
    GitSubmodulesRequirement,
)

class DependencyReporter:
    """
    Generates formatted dependency reports from JSON data with help text.

    CMake provides all dependency data via JSON. This class formats the output
    and generates platform-specific installation instructions for failures.
    """

    def __init__(self, deps_path: Path):
        # Load CMake variables
        cmake_vars = self._load_cmake_vars(deps_path)

        self.colors = Colors()
        self.platform_info = detect_platform()

        # Create requirement objects - each class defines its own name and variable names
        self.requirements = []
        self.requirements.append(NinjaRequirement(cmake_vars))
        self.requirements.append(GitSubmodulesRequirement(cmake_vars))
        self.requirements.append(CompilerRequirement(cmake_vars))
        self.requirements.append(PythonRequirement(cmake_vars))
        self.requirements.append(CUDAToolkitRequirement(cmake_vars))
        self.requirements.append(TorchRequirement(cmake_vars))
        self.requirements.append(Pybind11Requirement(cmake_vars))
        self.requirements.append(LLVMRequirement(cmake_vars))
        self.requirements.append(NVMMHRequirement(cmake_vars))

    def _load_cmake_vars(self, deps_path: Path) -> Dict:
        """Load CMake variables from JSON file"""
        try:
            with open(deps_path, "r") as f:
                data = json.load(f)
                return data.get("cmake_vars", {})
        except FileNotFoundError:
            print(f"Error: {deps_path} not found", file=sys.stderr)
            sys.exit(1)
        except Exception as e:
            print(f"Error loading dependencies: {e}", file=sys.stderr)
            sys.exit(1)

    def generate_report(self):
        """Main entry point - prints formatted report"""
        print()  # Blank line before report
        print("=" * 80)
        self._print_header()
        print("=" * 80)
        print()  # Blank line after header
        self._print_dependencies()
        print()  # Blank line after dependencies
        print("* Optional requirement")
        print("=" * 80)

        # Collect failures and issues to show help for
        # Include required failures (is_failure=True) and optional issues (not SUCCESS)
        failures = []
        for req in self.requirements:
            if hasattr(req, "is_failure") and req.is_failure():
                # Required dependency that failed
                failures.append(req)
            elif (
                hasattr(req, "status")
                and hasattr(req, "optional")
                and req.optional
                and req.status != "SUCCESS"
            ):
                # Optional dependency with issues (NOT_FOUND or INCOMPATIBLE)
                failures.append(req)

        if failures:
            self._print_help_section(failures)
            # Only print failure summary if there are actual (non-optional) failures
            required_failures = [
                req
                for req in self.requirements
                if hasattr(req, "is_failure") and req.is_failure()
            ]
            if required_failures:
                self._print_failure_summary()

        print()  # Blank line at end

    def _print_header(self):
        """Print report header with platform information"""
        platform_str = format_platform_info(self.platform_info)
        print(
            colorize(
                self.colors.BOLD_GREEN, "[nvFuser] Validating build prerequisites..."
            )
        )
        print(colorize(self.colors.CYAN, "Platform: ") + platform_str)

    def _print_failure_summary(self):
        """Print failure summary message"""
        print()
        print(colorize(self.colors.BOLD_RED, "Build prerequisite validation FAILED"))
        print("See installation instructions above")

    def _print_dependencies(self):
        """Print status for each dependency using OOP requirement classes"""
        for req in self.requirements:
            if hasattr(req, "format_status_line"):
                # OOP: use requirement's format method
                print(req.format_status_line(self.colors))
            else:
                # Fallback: use legacy dict-based formatting (shouldn't happen)
                print(f"[nvFuser] ? {getattr(req, 'name', 'Unknown')}")

    def _print_help_section(self, failures):
        """Print help section header and help for each failed dependency"""
        print()
        print("=" * 70)
        print("Installation Instructions")
        print("=" * 70)
        print()

        for req in failures:
            self._print_help_for_requirement(req)

    def _print_help_for_requirement(self, req):
        """Call requirement's help generation method"""
        if hasattr(req, "generate_help"):
            req.generate_help(self.platform_info)
        else:
            # Fallback for requirements without help
            print(f"{req.name} installation help not available")
            print()

def main():
    if len(sys.argv) != 2:
        print("Usage: check_dependencies.py <deps.json>", file=sys.stderr)
        sys.exit(1)

    json_path = Path(sys.argv[1])
    if not json_path.exists():
        print(f"Error: {json_path} not found", file=sys.stderr)
        sys.exit(1)

    # Generate report
    reporter = DependencyReporter(json_path)
    reporter.generate_report()

    # Report generated successfully
    sys.exit(0)


main

        """Print help section header and help for each failed dependency"""
        print()
        print("=" * 70)
        print("Installation Instructions")
        print("=" * 70)
        print()

        for req in failures:
            self._print_help_for_requirement(req)

    def _print_help_for_requirement(self, req):
        """Call requirement's help generation method"""
        if hasattr(req, "generate_help"):
            req.generate_help(self.platform_info)
        else:
            # Fallback for requirements without help
            print(f"{req.name} installation help not available")
            print()

def main():
    if len(sys.argv) != 2:
        print("Usage: check_dependencies.py <deps.json>", file=sys.stderr)
        sys.exit(1)

    json_path = Path(sys.argv[1])
    if not json_path.exists():
        print(f"Error: {json_path} not found", file=sys.stderr)
        sys.exit(1)

    # Generate report
    reporter = DependencyReporter(json_path)
    reporter.generate_report()

    # Report generated successfully
    sys.exit(0)

if __name__ == "__main__":
    main()


    PythonRequirement,
    TorchRequirement,
    LLVMRequirement,
    CUDAToolkitRequirement,
    Pybind11Requirement,
    CompilerRequirement,
    NinjaRequirement,
    NVMMHRequirement,
    GitSubmodulesRequirement,
)

class DependencyReporter:
    """
    Generates formatted dependency reports from JSON data with help text.

    CMake provides all dependency data via JSON. This class formats the output
    and generates platform-specific installation instructions for failures.
    """

    def __init__(self, deps_path: Path):
        # Load CMake variables
        cmake_vars = self._load_cmake_vars(deps_path)

        self.colors = Colors()
        self.platform_info = detect_platform()

        # Create requirement objects - each class defines its own name and variable names
        self.requirements = []
        self.requirements.append(NinjaRequirement(cmake_vars))
        self.requirements.append(GitSubmodulesRequirement(cmake_vars))
        self.requirements.append(CompilerRequirement(cmake_vars))
        self.requirements.append(PythonRequirement(cmake_vars))
        self.requirements.append(CUDAToolkitRequirement(cmake_vars))
        self.requirements.append(TorchRequirement(cmake_vars))
        self.requirements.append(Pybind11Requirement(cmake_vars))
        self.requirements.append(LLVMRequirement(cmake_vars))
        self.requirements.append(NVMMHRequirement(cmake_vars))

    def _load_cmake_vars(self, deps_path: Path) -> Dict:
        """Load CMake variables from JSON file"""
        try:
            with open(deps_path, "r") as f:
                data = json.load(f)
                return data.get("cmake_vars", {})
        except FileNotFoundError:
            print(f"Error: {deps_path} not found", file=sys.stderr)
            sys.exit(1)
        except Exception as e:
            print(f"Error loading dependencies: {e}", file=sys.stderr)
            sys.exit(1)

    def generate_report(self):
        """Main entry point - prints formatted report"""
        print()  # Blank line before report
        print("=" * 80)
        self._print_header()
        print("=" * 80)


    def __init__(self, deps_path: Path):
        # Load CMake variables
        cmake_vars = self._load_cmake_vars(deps_path)

        self.colors = Colors()
        self.platform_info = detect_platform()

        # Create requirement objects - each class defines its own name and variable names
        self.requirements = []
        self.requirements.append(NinjaRequirement(cmake_vars))
        self.requirements.append(GitSubmodulesRequirement(cmake_vars))
        self.requirements.append(CompilerRequirement(cmake_vars))
        self.requirements.append(PythonRequirement(cmake_vars))
        self.requirements.append(CUDAToolkitRequirement(cmake_vars))
        self.requirements.append(TorchRequirement(cmake_vars))
        self.requirements.append(Pybind11Requirement(cmake_vars))
        self.requirements.append(LLVMRequirement(cmake_vars))
        self.requirements.append(NVMMHRequirement(cmake_vars))

    def _load_cmake_vars(self, deps_path: Path) -> Dict:
        """Load CMake variables from JSON file"""
        try:
            with open(deps_path, "r") as f:
                data = json.load(f)
                return data.get("cmake_vars", {})
        except FileNotFoundError:
            print(f"Error: {deps_path} not found", file=sys.stderr)
            sys.exit(1)
        except Exception as e:
            print(f"Error loading dependencies: {e}", file=sys.stderr)
            sys.exit(1)

    def generate_report(self):
        """Main entry point - prints formatted report"""
        print()  # Blank line before report
        print("=" * 80)
        self._print_header()
        print("=" * 80)
        print()  # Blank line after header
        self._print_dependencies()
        print()  # Blank line after dependencies
        print("* Optional requirement")
        print("=" * 80)

        # Collect failures and issues to show help for
        # Include required failures (is_failure=True) and optional issues (not SUCCESS)
        failures = []
        for req in self.requirements:
            if hasattr(req, "is_failure") and req.is_failure():
                # Required dependency that failed
                failures.append(req)


        self.requirements.append(PythonRequirement(cmake_vars))
        self.requirements.append(CUDAToolkitRequirement(cmake_vars))
        self.requirements.append(TorchRequirement(cmake_vars))
        self.requirements.append(Pybind11Requirement(cmake_vars))
        self.requirements.append(LLVMRequirement(cmake_vars))
        self.requirements.append(NVMMHRequirement(cmake_vars))

    def _load_cmake_vars(self, deps_path: Path) -> Dict:
        """Load CMake variables from JSON file"""
        try:
            with open(deps_path, "r") as f:
                data = json.load(f)
                return data.get("cmake_vars", {})
        except FileNotFoundError:
            print(f"Error: {deps_path} not found", file=sys.stderr)
            sys.exit(1)
        except Exception as e:
            print(f"Error loading dependencies: {e}", file=sys.stderr)
            sys.exit(1)

    def generate_report(self):
        """Main entry point - prints formatted report"""
        print()  # Blank line before report
        print("=" * 80)
        self._print_header()
        print("=" * 80)
        print()  # Blank line after header
        self._print_dependencies()
        print()  # Blank line after dependencies
        print("* Optional requirement")
        print("=" * 80)

        # Collect failures and issues to show help for
        # Include required failures (is_failure=True) and optional issues (not SUCCESS)
        failures = []
        for req in self.requirements:
            if hasattr(req, "is_failure") and req.is_failure():
                # Required dependency that failed
                failures.append(req)
            elif (
                hasattr(req, "status")
                and hasattr(req, "optional")
                and req.optional
                and req.status != "SUCCESS"
            ):
                # Optional dependency with issues (NOT_FOUND or INCOMPATIBLE)
                failures.append(req)

        if failures:
            self._print_help_section(failures)
            # Only print failure summary if there are actual (non-optional) failures
            required_failures = [
                req
                for req in self.requirements
                if hasattr(req, "is_failure") and req.is_failure()
            ]
            if required_failures:
                self._print_failure_summary()

        print()  # Blank line at end

    def _print_header(self):
        """Print report header with platform information"""
        platform_str = format_platform_info(self.platform_info)
        print(
            colorize(
                self.colors.BOLD_GREEN, "[nvFuser] Validating build prerequisites..."
            )
        )
        print(colorize(self.colors.CYAN, "Platform: ") + platform_str)

    def _print_failure_summary(self):
        """Print failure summary message"""
        print()
        print(colorize(self.colors.BOLD_RED, "Build prerequisite validation FAILED"))
        print("See installation instructions above")

    def _print_dependencies(self):
        """Print status for each dependency using OOP requirement classes"""
        for req in self.requirements:


                and hasattr(req, "optional")
                and req.optional
                and req.status != "SUCCESS"
            ):
                # Optional dependency with issues (NOT_FOUND or INCOMPATIBLE)
                failures.append(req)

        if failures:
            self._print_help_section(failures)
            # Only print failure summary if there are actual (non-optional) failures
            required_failures = [
                req
                for req in self.requirements
                if hasattr(req, "is_failure") and req.is_failure()
            ]
            if required_failures:
                self._print_failure_summary()

        print()  # Blank line at end

    def _print_header(self):
        """Print report header with platform information"""
        platform_str = format_platform_info(self.platform_info)
        print(
            colorize(
                self.colors.BOLD_GREEN, "[nvFuser] Validating build prerequisites..."
            )
        )
        print(colorize(self.colors.CYAN, "Platform: ") + platform_str)

    def _print_failure_summary(self):
        """Print failure summary message"""
        print()
        print(colorize(self.colors.BOLD_RED, "Build prerequisite validation FAILED"))
        print("See installation instructions above")

    def _print_dependencies(self):
        """Print status for each dependency using OOP requirement classes"""
        for req in self.requirements:
            if hasattr(req, "format_status_line"):
                # OOP: use requirement's format method
                print(req.format_status_line(self.colors))
            else:
                # Fallback: use legacy dict-based formatting (shouldn't happen)
                print(f"[nvFuser] ? {getattr(req, 'name', 'Unknown')}")

    def _print_help_section(self, failures):
        """Print help section header and help for each failed dependency"""
        print()


            required_failures = [
                req
                for req in self.requirements
                if hasattr(req, "is_failure") and req.is_failure()
            ]
            if required_failures:
                self._print_failure_summary()

        print()  # Blank line at end

    def _print_header(self):
        """Print report header with platform information"""
        platform_str = format_platform_info(self.platform_info)
        print(
            colorize(
                self.colors.BOLD_GREEN, "[nvFuser] Validating build prerequisites..."
            )
        )
        print(colorize(self.colors.CYAN, "Platform: ") + platform_str)

    def _print_failure_summary(self):
        """Print failure summary message"""
        print()
        print(colorize(self.colors.BOLD_RED, "Build prerequisite validation FAILED"))
        print("See installation instructions above")

    def _print_dependencies(self):
        """Print status for each dependency using OOP requirement classes"""
        for req in self.requirements:
            if hasattr(req, "format_status_line"):
                # OOP: use requirement's format method
                print(req.format_status_line(self.colors))
            else:
                # Fallback: use legacy dict-based formatting (shouldn't happen)
                print(f"[nvFuser] ? {getattr(req, 'name', 'Unknown')}")

    def _print_help_section(self, failures):
        """Print help section header and help for each failed dependency"""
        print()
        print("=" * 70)
        print("Installation Instructions")
        print("=" * 70)
        print()

        for req in failures:


                self._print_failure_summary()

        print()  # Blank line at end

    def _print_header(self):
        """Print report header with platform information"""
        platform_str = format_platform_info(self.platform_info)
        print(
            colorize(
                self.colors.BOLD_GREEN, "[nvFuser] Validating build prerequisites..."
            )
        )
        print(colorize(self.colors.CYAN, "Platform: ") + platform_str)

    def _print_failure_summary(self):
        """Print failure summary message"""
        print()
        print(colorize(self.colors.BOLD_RED, "Build prerequisite validation FAILED"))
        print("See installation instructions above")

    def _print_dependencies(self):
        """Print status for each dependency using OOP requirement classes"""
        for req in self.requirements:
            if hasattr(req, "format_status_line"):
                # OOP: use requirement's format method
                print(req.format_status_line(self.colors))
            else:
                # Fallback: use legacy dict-based formatting (shouldn't happen)
                print(f"[nvFuser] ? {getattr(req, 'name', 'Unknown')}")

    def _print_help_section(self, failures):
        """Print help section header and help for each failed dependency"""
        print()
        print("=" * 70)
        print("Installation Instructions")
        print("=" * 70)
        print()

        for req in failures:
            self._print_help_for_requirement(req)

    def _print_help_for_requirement(self, req):
        """Call requirement's help generation method"""
        if hasattr(req, "generate_help"):
            req.generate_help(self.platform_info)
        else:
            # Fallback for requirements without help
            print(f"{req.name} installation help not available")
            print()


            )
        )
        print(colorize(self.colors.CYAN, "Platform: ") + platform_str)

    def _print_failure_summary(self):
        """Print failure summary message"""
        print()
        print(colorize(self.colors.BOLD_RED, "Build prerequisite validation FAILED"))
        print("See installation instructions above")

    def _print_dependencies(self):
        """Print status for each dependency using OOP requirement classes"""
        for req in self.requirements:
            if hasattr(req, "format_status_line"):
                # OOP: use requirement's format method
                print(req.format_status_line(self.colors))
            else:
                # Fallback: use legacy dict-based formatting (shouldn't happen)
                print(f"[nvFuser] ? {getattr(req, 'name', 'Unknown')}")

    def _print_help_section(self, failures):
        """Print help section header and help for each failed dependency"""
        print()
        print("=" * 70)
        print("Installation Instructions")
        print("=" * 70)
        print()

        for req in failures:
            self._print_help_for_requirement(req)

    def _print_help_for_requirement(self, req):
        """Call requirement's help generation method"""
        if hasattr(req, "generate_help"):
            req.generate_help(self.platform_info)
        else:
            # Fallback for requirements without help
            print(f"{req.name} installation help not available")
            print()

def main():
    if len(sys.argv) != 2:
        print("Usage: check_dependencies.py <deps.json>", file=sys.stderr)
        sys.exit(1)

    json_path = Path(sys.argv[1])
    if not json_path.exists():
        print(f"Error: {json_path} not found", file=sys.stderr)
        sys.exit(1)


        """Print status for each dependency using OOP requirement classes"""
        for req in self.requirements:
            if hasattr(req, "format_status_line"):
                # OOP: use requirement's format method
                print(req.format_status_line(self.colors))
            else:
                # Fallback: use legacy dict-based formatting (shouldn't happen)
                print(f"[nvFuser] ? {getattr(req, 'name', 'Unknown')}")

    def _print_help_section(self, failures):
        """Print help section header and help for each failed dependency"""
        print()
        print("=" * 70)
        print("Installation Instructions")
        print("=" * 70)
        print()

        for req in failures:
            self._print_help_for_requirement(req)

    def _print_help_for_requirement(self, req):
        """Call requirement's help generation method"""
        if hasattr(req, "generate_help"):
            req.generate_help(self.platform_info)
        else:
            # Fallback for requirements without help
            print(f"{req.name} installation help not available")
            print()

def main():
    if len(sys.argv) != 2:
        print("Usage: check_dependencies.py <deps.json>", file=sys.stderr)
        sys.exit(1)

    json_path = Path(sys.argv[1])
    if not json_path.exists():
        print(f"Error: {json_path} not found", file=sys.stderr)
        sys.exit(1)

    # Generate report
    reporter = DependencyReporter(json_path)
    reporter.generate_report()

    # Report generated successfully
    sys.exit(0)


# SPDX-FileCopyrightText: Copyright (c) 2023-present NVIDIA CORPORATION & AFFILIATES.
# All rights reserved.
# SPDX-License-Identifier: BSD-3-Clause
import subprocess
import sys
from pathlib import Path

UNKNOWN = "Unknown"
nvfuser_root = Path(__file__).parent.parent

# note that this root currently is still part of pytorch.
def get_sha() -> str:
    try:
        return (
            subprocess.check_output(["git", "rev-parse", "HEAD"], cwd=nvfuser_root)
            .decode("ascii")
            .strip()
        )
    except Exception:
        import os

        # assume the $NVFUSER_VERSION is in sha form
        if nvfuser_version := os.environ.get("NVFUSER_VERSION"):
            assert (
                len(nvfuser_version) < 11
            ), "The NVFUSER_VERSION should be in sha form"
            return nvfuser_version
        return UNKNOWN

def get_version() -> str:
    sha = get_sha()
    version = (
        open((nvfuser_root / "version.txt"), "r").read().strip() + "+git" + sha[:7]
    )
    return version

def get_pytorch_cmake_prefix():
    from subprocess import Popen, PIPE

    # need to do this in a separate process so we are not going to delete nvfuser library while it's loaded by torch
    process_torch_prefix = Popen(
        [
            sys.executable,
            "-c",
            "import torch.utils; print(torch.utils.cmake_prefix_path)",
        ],
        stdout=PIPE,
    )
    stdout_msg, error_msg = process_torch_prefix.communicate()
    return stdout_msg.decode("utf-8").rstrip("\n")

def get_pytorch_use_distributed():
    from subprocess import Popen, PIPE

    # need to do this in a separate process so we are not going to delete nvfuser library while it's loaded by torch
    process_torch_prefix = Popen(
        [
            sys.executable,
            "-c",
            "import torch; print(torch._C._has_distributed())",
        ],
        stdout=PIPE,
    )
    stdout_msg, error_msg = process_torch_prefix.communicate()
    return stdout_msg.decode("utf-8").rstrip("\n")

if __name__ == "__main__":
    assert len(sys.argv) == 2
    assert sys.argv[1] == "nvfuser" or sys.argv[1] == "nvfuser_direct"
    python_module = sys.argv[1]
    version_file = nvfuser_root / python_module / "version.py"
    with open(version_file, "w") as f:
        f.write("_version_str = '{}'\n".format(get_version()))


# SPDX-FileCopyrightText: Copyright (c) 2023-present NVIDIA CORPORATION & AFFILIATES.
# All rights reserved.
# SPDX-License-Identifier: BSD-3-Clause
import subprocess
import sys
from pathlib import Path

UNKNOWN = "Unknown"
nvfuser_root = Path(__file__).parent.parent

# note that this root currently is still part of pytorch.
def get_sha() -> str:
    try:
        return (
            subprocess.check_output(["git", "rev-parse", "HEAD"], cwd=nvfuser_root)
            .decode("ascii")
            .strip()
        )
    except Exception:
        import os

        # assume the $NVFUSER_VERSION is in sha form
        if nvfuser_version := os.environ.get("NVFUSER_VERSION"):
            assert (
                len(nvfuser_version) < 11
            ), "The NVFUSER_VERSION should be in sha form"
            return nvfuser_version
        return UNKNOWN

def get_version() -> str:
    sha = get_sha()
    version = (
        open((nvfuser_root / "version.txt"), "r").read().strip() + "+git" + sha[:7]
    )
    return version

def get_pytorch_cmake_prefix():
    from subprocess import Popen, PIPE

    # need to do this in a separate process so we are not going to delete nvfuser library while it's loaded by torch
    process_torch_prefix = Popen(
        [
            sys.executable,
            "-c",
            "import torch.utils; print(torch.utils.cmake_prefix_path)",
        ],


# note that this root currently is still part of pytorch.
def get_sha() -> str:
    try:
        return (
            subprocess.check_output(["git", "rev-parse", "HEAD"], cwd=nvfuser_root)
            .decode("ascii")
            .strip()
        )
    except Exception:
        import os

        # assume the $NVFUSER_VERSION is in sha form
        if nvfuser_version := os.environ.get("NVFUSER_VERSION"):
            assert (
                len(nvfuser_version) < 11
            ), "The NVFUSER_VERSION should be in sha form"
            return nvfuser_version
        return UNKNOWN

def get_version() -> str:
    sha = get_sha()
    version = (
        open((nvfuser_root / "version.txt"), "r").read().strip() + "+git" + sha[:7]
    )
    return version

def get_pytorch_cmake_prefix():
    from subprocess import Popen, PIPE

    # need to do this in a separate process so we are not going to delete nvfuser library while it's loaded by torch
    process_torch_prefix = Popen(
        [
            sys.executable,
            "-c",
            "import torch.utils; print(torch.utils.cmake_prefix_path)",
        ],
        stdout=PIPE,
    )
    stdout_msg, error_msg = process_torch_prefix.communicate()
    return stdout_msg.decode("utf-8").rstrip("\n")

def get_pytorch_use_distributed():
    from subprocess import Popen, PIPE


    except Exception:
        import os

        # assume the $NVFUSER_VERSION is in sha form
        if nvfuser_version := os.environ.get("NVFUSER_VERSION"):
            assert (
                len(nvfuser_version) < 11
            ), "The NVFUSER_VERSION should be in sha form"
            return nvfuser_version
        return UNKNOWN

def get_version() -> str:
    sha = get_sha()
    version = (
        open((nvfuser_root / "version.txt"), "r").read().strip() + "+git" + sha[:7]
    )
    return version

def get_pytorch_cmake_prefix():
    from subprocess import Popen, PIPE

    # need to do this in a separate process so we are not going to delete nvfuser library while it's loaded by torch
    process_torch_prefix = Popen(
        [
            sys.executable,
            "-c",
            "import torch.utils; print(torch.utils.cmake_prefix_path)",
        ],
        stdout=PIPE,
    )
    stdout_msg, error_msg = process_torch_prefix.communicate()
    return stdout_msg.decode("utf-8").rstrip("\n")

def get_pytorch_use_distributed():
    from subprocess import Popen, PIPE

    # need to do this in a separate process so we are not going to delete nvfuser library while it's loaded by torch
    process_torch_prefix = Popen(
        [
            sys.executable,
            "-c",
            "import torch; print(torch._C._has_distributed())",
        ],
        stdout=PIPE,
    )
    stdout_msg, error_msg = process_torch_prefix.communicate()
    return stdout_msg.decode("utf-8").rstrip("\n")

if __name__ == "__main__":
    assert len(sys.argv) == 2


    )
    return version

def get_pytorch_cmake_prefix():
    from subprocess import Popen, PIPE

    # need to do this in a separate process so we are not going to delete nvfuser library while it's loaded by torch
    process_torch_prefix = Popen(
        [
            sys.executable,
            "-c",
            "import torch.utils; print(torch.utils.cmake_prefix_path)",
        ],
        stdout=PIPE,
    )
    stdout_msg, error_msg = process_torch_prefix.communicate()
    return stdout_msg.decode("utf-8").rstrip("\n")

def get_pytorch_use_distributed():
    from subprocess import Popen, PIPE

    # need to do this in a separate process so we are not going to delete nvfuser library while it's loaded by torch
    process_torch_prefix = Popen(
        [
            sys.executable,
            "-c",
            "import torch; print(torch._C._has_distributed())",
        ],
        stdout=PIPE,
    )
    stdout_msg, error_msg = process_torch_prefix.communicate()
    return stdout_msg.decode("utf-8").rstrip("\n")

if __name__ == "__main__":
    assert len(sys.argv) == 2
    assert sys.argv[1] == "nvfuser" or sys.argv[1] == "nvfuser_direct"
    python_module = sys.argv[1]
    version_file = nvfuser_root / python_module / "version.py"
    with open(version_file, "w") as f:
        f.write("_version_str = '{}'\n".format(get_version()))


python.tools.prereqs.colors

# SPDX-FileCopyrightText: Copyright (c) 2025-present NVIDIA CORPORATION & AFFILIATES.
# All rights reserved.
# SPDX-License-Identifier: BSD-3-Clause
"""
Color utilities for terminal output.
"""

import os

class Colors:
    """ANSI color codes for terminal output"""

    _codes = {
        "RESET": "\033[m",
        "BOLD": "\033[1m",
        # Regular colors
        "GREEN": "\033[32m",
        "YELLOW": "\033[33m",
        "CYAN": "\033[36m",
        "WHITE": "\033[37m",
        # Bold colors
        "BOLD_RED": "\033[1;31m",
        "BOLD_GREEN": "\033[1;32m",
        "BOLD_WHITE": "\033[1;37m",
    }

    def __init__(self):
        use_colors = os.environ.get("NVFUSER_BUILD_DISABLE_COLOR") is None

        for name, code in self._codes.items():
            setattr(self, name, code if use_colors else "")

def colorize(color: str, text: str) -> str:
    """Helper to wrap text with color and reset codes.

    Args:
        color: The color code (e.g., colors.GREEN, colors.BOLD_RED)
        text: The text to colorize

    Returns:
        Text wrapped with color codes: <color>text<reset>

    Example:
        >>> colors = Colors()
        >>> print(colorize(colors.GREEN, "Success") + " - operation completed")
        # Prints "Success" in green, followed by plain text
    """
    RESET = "\033[m"
    return f"{color}{text}{RESET}"


colorize

        "RESET": "\033[m",
        "BOLD": "\033[1m",
        # Regular colors
        "GREEN": "\033[32m",
        "YELLOW": "\033[33m",
        "CYAN": "\033[36m",
        "WHITE": "\033[37m",
        # Bold colors
        "BOLD_RED": "\033[1;31m",
        "BOLD_GREEN": "\033[1;32m",
        "BOLD_WHITE": "\033[1;37m",
    }

    def __init__(self):
        use_colors = os.environ.get("NVFUSER_BUILD_DISABLE_COLOR") is None

        for name, code in self._codes.items():
            setattr(self, name, code if use_colors else "")

def colorize(color: str, text: str) -> str:
    """Helper to wrap text with color and reset codes.

    Args:
        color: The color code (e.g., colors.GREEN, colors.BOLD_RED)
        text: The text to colorize

    Returns:
        Text wrapped with color codes: <color>text<reset>

    Example:
        >>> colors = Colors()
        >>> print(colorize(colors.GREEN, "Success") + " - operation completed")
        # Prints "Success" in green, followed by plain text
    """
    RESET = "\033[m"
    return f"{color}{text}{RESET}"


import os

class Colors:
    """ANSI color codes for terminal output"""

    _codes = {
        "RESET": "\033[m",
        "BOLD": "\033[1m",
        # Regular colors
        "GREEN": "\033[32m",
        "YELLOW": "\033[33m",
        "CYAN": "\033[36m",
        "WHITE": "\033[37m",
        # Bold colors
        "BOLD_RED": "\033[1;31m",
        "BOLD_GREEN": "\033[1;32m",
        "BOLD_WHITE": "\033[1;37m",
    }

    def __init__(self):
        use_colors = os.environ.get("NVFUSER_BUILD_DISABLE_COLOR") is None

        for name, code in self._codes.items():
            setattr(self, name, code if use_colors else "")

def colorize(color: str, text: str) -> str:
    """Helper to wrap text with color and reset codes.

    Args:
        color: The color code (e.g., colors.GREEN, colors.BOLD_RED)
        text: The text to colorize

    Returns:
        Text wrapped with color codes: <color>text<reset>

    Example:
        >>> colors = Colors()
        >>> print(colorize(colors.GREEN, "Success") + " - operation completed")
        # Prints "Success" in green, followed by plain text
    """
    RESET = "\033[m"
    return f"{color}{text}{RESET}"


python.tools.prereqs.exceptions

# SPDX-FileCopyrightText: Copyright (c) 2025-present NVIDIA CORPORATION & AFFILIATES.
# All rights reserved.
# SPDX-License-Identifier: BSD-3-Clause
"""
Custom exceptions for nvFuser prerequisite validation.

These exceptions provide structured error handling for build prerequisite
checks, enabling clear and actionable error messages.
"""

class PrerequisiteMissingError(Exception):
    """
    Raised when a prerequisite for building nvFuser is missing or has an incorrect version.

    This exception should include:
    - What prerequisite is missing or incorrect
    - Why it's required
    - Exact commands to install or fix it
    - Platform-specific guidance when applicable
    """

    pass


python.tools.prereqs.platform

# SPDX-FileCopyrightText: Copyright (c) 2025-present NVIDIA CORPORATION & AFFILIATES.
# All rights reserved.
# SPDX-License-Identifier: BSD-3-Clause
"""
Platform detection utilities for nvFuser build system.

Detects OS, architecture, and Linux distribution to provide platform-specific
error messages and installation guidance.
"""

import platform
from typing import Dict, Optional

def detect_platform() -> Dict[str, Optional[str]]:
    """
    Detect the current platform and return structured information.

    Returns:
        dict: Platform information with keys:
            - 'os': Operating system (Linux, Darwin, Windows, etc.)
            - 'arch': Architecture (x86_64, aarch64, arm64, etc.)
            - 'distro': Linux distribution ID (ubuntu, debian, rhel, etc.) or None
            - 'distro_version': Distribution version (22.04, 20.04, etc.) or None
            - 'distro_name': Human-readable distribution name or None
            - 'ubuntu_based': Boolean indicating if this is Ubuntu-based distro

    Example:
        >>> info = detect_platform()
        >>> print(info['os'])
        'Linux'
        >>> print(info['distro'])
        'ubuntu'
    """
    system = platform.system()
    machine = platform.machine()

    # Initialize distro information
    distro_info = {}
    distro_id = None
    distro_version = None
    distro_name = None
    ubuntu_based = False

    # Detect Linux distribution from /etc/os-release
    if system == "Linux":
        try:
            with open("/etc/os-release") as f:
                for line in f:
                    line = line.strip()
                    if "=" in line:
                        key, value = line.split("=", 1)
                        # Remove quotes from value
                        distro_info[key] = value.strip('"').strip("'")

            distro_id = distro_info.get("ID", "unknown")
            distro_version = distro_info.get("VERSION_ID", "unknown")
            distro_name = distro_info.get("NAME", "unknown")

            # Check if Ubuntu-based (useful for PPA availability)
            ubuntu_based = distro_id in (
                "ubuntu",
                "debian",
                "linuxmint",
                "pop",
                "zorin",
            )

        except FileNotFoundError:
            # /etc/os-release doesn't exist (not a standard Linux or very old system)
            distro_id = "unknown"
            distro_version = "unknown"
            distro_name = "unknown"
        except Exception as e:
            # Other errors reading/parsing the file
            distro_id = f"error: {e}"
            distro_version = "unknown"
            distro_name = "unknown"

    return {
        "os": system,
        "arch": machine,
        "distro": distro_id,
        "distro_version": distro_version,
        "distro_name": distro_name,
        "ubuntu_based": ubuntu_based,
    }

def format_platform_info(
    platform_info: Optional[Dict[str, Optional[str]]] = None
) -> str:
    """
    Format platform information as a human-readable string.

    Args:
        platform_info: Platform information dict from detect_platform().
                      If None, will call detect_platform() automatically.

    Returns:
        str: Formatted platform string like "Linux x86_64 (Ubuntu 22.04)"

    Example:
        >>> print(format_platform_info())
        'Linux x86_64 (Ubuntu 22.04)'
    """
    if platform_info is None:
        platform_info = detect_platform()

    os_name = platform_info["os"]
    arch = platform_info["arch"]

    # Build distro info if available
    distro_parts = []
    if platform_info.get("distro") and platform_info["distro"] not in (
        "unknown",
        "error",
    ):
        distro_parts.append(platform_info["distro"].capitalize())
    if (
        platform_info.get("distro_version")
        and platform_info["distro_version"] != "unknown"
    ):
        distro_parts.append(platform_info["distro_version"])

    if distro_parts:
        return f"{os_name} {arch} ({' '.join(distro_parts)})"
    else:
        return f"{os_name} {arch}"


# SPDX-FileCopyrightText: Copyright (c) 2025-present NVIDIA CORPORATION & AFFILIATES.
# All rights reserved.
# SPDX-License-Identifier: BSD-3-Clause
"""
Platform detection utilities for nvFuser build system.

Detects OS, architecture, and Linux distribution to provide platform-specific
error messages and installation guidance.
"""

import platform
from typing import Dict, Optional

def detect_platform() -> Dict[str, Optional[str]]:
    """
    Detect the current platform and return structured information.

    Returns:
        dict: Platform information with keys:
            - 'os': Operating system (Linux, Darwin, Windows, etc.)
            - 'arch': Architecture (x86_64, aarch64, arm64, etc.)
            - 'distro': Linux distribution ID (ubuntu, debian, rhel, etc.) or None
            - 'distro_version': Distribution version (22.04, 20.04, etc.) or None
            - 'distro_name': Human-readable distribution name or None
            - 'ubuntu_based': Boolean indicating if this is Ubuntu-based distro

    Example:
        >>> info = detect_platform()
        >>> print(info['os'])
        'Linux'
        >>> print(info['distro'])
        'ubuntu'
    """
    system = platform.system()
    machine = platform.machine()

    # Initialize distro information
    distro_info = {}
    distro_id = None
    distro_version = None
    distro_name = None
    ubuntu_based = False

    # Detect Linux distribution from /etc/os-release
    if system == "Linux":
        try:
            with open("/etc/os-release") as f:
                for line in f:
                    line = line.strip()
                    if "=" in line:
                        key, value = line.split("=", 1)
                        # Remove quotes from value
                        distro_info[key] = value.strip('"').strip("'")

            distro_id = distro_info.get("ID", "unknown")
            distro_version = distro_info.get("VERSION_ID", "unknown")
            distro_name = distro_info.get("NAME", "unknown")

            # Check if Ubuntu-based (useful for PPA availability)
            ubuntu_based = distro_id in (
                "ubuntu",
                "debian",
                "linuxmint",
                "pop",
                "zorin",
            )

        except FileNotFoundError:
            # /etc/os-release doesn't exist (not a standard Linux or very old system)
            distro_id = "unknown"
            distro_version = "unknown"
            distro_name = "unknown"
        except Exception as e:
            # Other errors reading/parsing the file
            distro_id = f"error: {e}"
            distro_version = "unknown"
            distro_name = "unknown"

    return {
        "os": system,
        "arch": machine,
        "distro": distro_id,
        "distro_version": distro_version,
        "distro_name": distro_name,
        "ubuntu_based": ubuntu_based,
    }

def format_platform_info(
    platform_info: Optional[Dict[str, Optional[str]]] = None
) -> str:
    """
    Format platform information as a human-readable string.

    Args:
        platform_info: Platform information dict from detect_platform().
                      If None, will call detect_platform() automatically.

    Returns:
        str: Formatted platform string like "Linux x86_64 (Ubuntu 22.04)"

    Example:
        >>> print(format_platform_info())
        'Linux x86_64 (Ubuntu 22.04)'
    """
    if platform_info is None:


            # /etc/os-release doesn't exist (not a standard Linux or very old system)
            distro_id = "unknown"
            distro_version = "unknown"
            distro_name = "unknown"
        except Exception as e:
            # Other errors reading/parsing the file
            distro_id = f"error: {e}"
            distro_version = "unknown"
            distro_name = "unknown"

    return {
        "os": system,
        "arch": machine,
        "distro": distro_id,
        "distro_version": distro_version,
        "distro_name": distro_name,
        "ubuntu_based": ubuntu_based,
    }

def format_platform_info(
    platform_info: Optional[Dict[str, Optional[str]]] = None
) -> str:
    """
    Format platform information as a human-readable string.

    Args:
        platform_info: Platform information dict from detect_platform().
                      If None, will call detect_platform() automatically.

    Returns:
        str: Formatted platform string like "Linux x86_64 (Ubuntu 22.04)"

    Example:
        >>> print(format_platform_info())
        'Linux x86_64 (Ubuntu 22.04)'
    """
    if platform_info is None:
        platform_info = detect_platform()

    os_name = platform_info["os"]
    arch = platform_info["arch"]

    # Build distro info if available
    distro_parts = []
    if platform_info.get("distro") and platform_info["distro"] not in (
        "unknown",
        "error",
    ):
        distro_parts.append(platform_info["distro"].capitalize())
    if (
        platform_info.get("distro_version")
        and platform_info["distro_version"] != "unknown"
    ):
        distro_parts.append(platform_info["distro_version"])

    if distro_parts:
        return f"{os_name} {arch} ({' '.join(distro_parts)})"
    else:
        return f"{os_name} {arch}"


# SPDX-FileCopyrightText: Copyright (c) 2025-present NVIDIA CORPORATION & AFFILIATES.
# All rights reserved.
# SPDX-License-Identifier: BSD-3-Clause
"""
Utility functions for nvFuser dependency reporting.

IMPORTANT: This module provides ONLY utility functions for formatting and URL generation.
Version requirements and dependency validation are handled by CMake.

CMake defines requirements in: cmake/DependencyRequirements.cmake
CMake exports status to: build/nvfuser_dependencies.json
Python reads JSON and uses these utilities to format help text.
"""

import platform
import re
from dataclasses import dataclass
from typing import Optional, Tuple

# =============================================================================
# VERSION CONVERSION UTILITIES
# =============================================================================

def parse_version(version_str: str) -> Tuple[int, ...]:
    """
    Parse version string to tuple.

    Args:
        version_str: Version string like "3.8", "18.1.8", "13", "18.1.8git"

    Returns:
        Tuple of integers: (3, 8), (18, 1, 8), (13,), (18, 1, 8)

    Examples:
        >>> parse_version("3.8")
        (3, 8)
        >>> parse_version("18.1.8")
        (18, 1, 8)
        >>> parse_version("18.1.8git")  # strips non-numeric suffix
        (18, 1, 8)

    Raises:
        ValueError: If version string cannot be parsed
    """
    # Strip common suffixes like "git", "rc1", "+cu128", etc.
    clean = re.match(r"^[\d.]+", version_str.strip())
    if not clean:
        raise ValueError(f"Cannot parse version: {version_str}")

    parts = clean.group().rstrip(".").split(".")
    return tuple(int(p) for p in parts if p)

def format_version(version: Tuple[int, ...]) -> str:
    """
    Format version tuple to string.

    Args:
        version: Tuple of integers like (3, 8), (18, 1, 8), (13,)

    Returns:
        Version string: "3.8", "18.1.8", "13"

    Examples:
        >>> format_version((3, 8))
        '3.8'
        >>> format_version((18, 1, 8))
        '18.1.8'
        >>> format_version((13,))
        '13'
    """
    return ".".join(map(str, version))

# =============================================================================
# REQUIREMENT DATACLASS (Utility Only)
# =============================================================================

@dataclass
class Requirement:
    """
    A version requirement utility class (NOT the source of truth).

    NOTE: This is for utility methods only. Actual version requirements come from
    CMake's DependencyRequirements.cmake and are exported via JSON.

    Attributes:
        name: Human-readable name (e.g., "CMake", "LLVM")
        min_version: Minimum required version tuple, or None for "any version"
        recommended: Recommended version tuple for download URLs (optional)
    """

    name: str
    min_version: Optional[Tuple[int, ...]]
    recommended: Optional[Tuple[int, ...]] = None

    @property
    def min_str(self) -> str:
        """Minimum version as string: '3.18' or 'any'"""
        if self.min_version is None:
            return "any"
        return format_version(self.min_version)

    @property
    def min_display(self) -> str:
        """Minimum version for display: '3.18+' or 'any version'"""
        if self.min_version is None:
            return "any version"
        return f"{self.min_str}+"

    @property
    def recommended_str(self) -> str:
        """Recommended version as string, falls back to min_str"""
        if self.recommended is None:
            return self.min_str
        return format_version(self.recommended)

    def check(self, detected: Tuple[int, ...]) -> bool:
        """
        Check if detected version meets minimum requirement.

        Args:
            detected: Detected version tuple (e.g., from parse_version)

        Returns:
            True if detected >= min_version (or min_version is None)

        Note:
            Compares only as many parts as min_version specifies.
            So (3, 22, 1) >= (3, 18) compares (3, 22) >= (3, 18) -> True
        """
        if self.min_version is None:
            return True
        # Compare only as many parts as min_version specifies
        return detected[: len(self.min_version)] >= self.min_version

# =============================================================================
# CUDA VERSIONS - For PyTorch wheel URLs
# =============================================================================

# PyTorch wheel CUDA versions currently available (newest first)
CUDA_AVAILABLE = [(13, 1), (13, 0), (12, 8)]

# =============================================================================
# URL GENERATORS
# =============================================================================

def cuda_wheel_suffix(cuda: Tuple[int, int]) -> str:
    """
    Convert CUDA version tuple to PyTorch wheel suffix.

    Examples:
        >>> cuda_wheel_suffix((12, 8))
        'cu128'
        >>> cuda_wheel_suffix((13, 1))
        'cu131'
    """
    return f"cu{cuda[0]}{cuda[1]}"

def pytorch_index_url(cuda: Tuple[int, int]) -> str:
    """
    Generate PyTorch wheel index URL for a CUDA version.

    Examples:
        >>> pytorch_index_url((12, 8))
        'https://download.pytorch.org/whl/cu128'
        >>> pytorch_index_url((13, 1))
        'https://download.pytorch.org/whl/cu131'
    """
    return f"https://download.pytorch.org/whl/{cuda_wheel_suffix(cuda)}"

def pytorch_install_instructions(cuda_major: Optional[int] = None) -> str:
    """
    Generate PyTorch installation instructions.

    Args:
        cuda_major: If specified, only show instructions for this CUDA major version.
                   Otherwise show all available versions.

    Returns:
        Multi-line string with pip install commands
    """
    if cuda_major is not None:
        # Filter to matching CUDA major version
        matching = [cuda for cuda in CUDA_AVAILABLE if cuda[0] == cuda_major]
        versions_to_show = matching if matching else CUDA_AVAILABLE
    else:
        versions_to_show = CUDA_AVAILABLE

    lines = []
    for cuda in versions_to_show:
        lines.append(f"  # For CUDA {format_version(cuda)}:")
        lines.append(f"  pip install torch --index-url {pytorch_index_url(cuda)}")
    return "\n".join(lines)

def llvm_download_url(version: Optional[Tuple[int, ...]] = None) -> str:
    """
    Generate LLVM prebuilt binary download URL.

    Args:
        version: LLVM version tuple. If None, uses (18, 1, 8) as default.

    Returns:
        GitHub release URL for prebuilt binary matching current platform

    Raises:
        NotImplementedError: If platform is not supported

    Example:
        >>> llvm_download_url((18, 1, 8))  # doctest: +SKIP
        'https://github.com/llvm/llvm-project/releases/download/llvmorg-18.1.8/clang+llvm-18.1.8-x86_64-linux-gnu-ubuntu-18.04.tar.xz'
    """
    if version is None:
        version = (18, 1, 8)

    v = format_version(version)
    machine = platform.machine()

    if machine == "x86_64":
        return (
            f"https://github.com/llvm/llvm-project/releases/download/"
            f"llvmorg-{v}/clang+llvm-{v}-x86_64-linux-gnu-ubuntu-18.04.tar.xz"
        )
    elif machine == "aarch64":
        return (
            f"https://github.com/llvm/llvm-project/releases/download/"
            f"llvmorg-{v}/clang+llvm-{v}-aarch64-linux-gnu.tar.xz"
        )
    elif machine.startswith("arm64"):
        # 64-bit ARM (macOS)
        return (
            f"https://github.com/llvm/llvm-project/releases/download/"
            f"llvmorg-{v}/clang+llvm-{v}-arm64-apple-macos11.tar.xz"
        )
    else:
        raise NotImplementedError(
            f"LLVM prebuilt binaries not available for: {machine}"
        )

def cuda_toolkit_download_url() -> str:
    """
    Return NVIDIA CUDA Toolkit download page URL.

    Returns:
        URL to CUDA downloads page
    """
    return "https://developer.nvidia.com/cuda-downloads"


IMPORTANT: This module provides ONLY utility functions for formatting and URL generation.
Version requirements and dependency validation are handled by CMake.

CMake defines requirements in: cmake/DependencyRequirements.cmake
CMake exports status to: build/nvfuser_dependencies.json
Python reads JSON and uses these utilities to format help text.
"""

import platform
import re
from dataclasses import dataclass
from typing import Optional, Tuple

# =============================================================================
# VERSION CONVERSION UTILITIES
# =============================================================================

def parse_version(version_str: str) -> Tuple[int, ...]:
    """
    Parse version string to tuple.

    Args:
        version_str: Version string like "3.8", "18.1.8", "13", "18.1.8git"

    Returns:
        Tuple of integers: (3, 8), (18, 1, 8), (13,), (18, 1, 8)

    Examples:
        >>> parse_version("3.8")
        (3, 8)
        >>> parse_version("18.1.8")
        (18, 1, 8)
        >>> parse_version("18.1.8git")  # strips non-numeric suffix
        (18, 1, 8)

    Raises:
        ValueError: If version string cannot be parsed
    """
    # Strip common suffixes like "git", "rc1", "+cu128", etc.
    clean = re.match(r"^[\d.]+", version_str.strip())
    if not clean:
        raise ValueError(f"Cannot parse version: {version_str}")

    parts = clean.group().rstrip(".").split(".")
    return tuple(int(p) for p in parts if p)

def format_version(version: Tuple[int, ...]) -> str:
    """
    Format version tuple to string.

    Args:
        version: Tuple of integers like (3, 8), (18, 1, 8), (13,)

    Returns:
        Version string: "3.8", "18.1.8", "13"

    Examples:
        >>> format_version((3, 8))
        '3.8'
        >>> format_version((18, 1, 8))
        '18.1.8'
        >>> format_version((13,))
        '13'
    """


    Examples:
        >>> parse_version("3.8")
        (3, 8)
        >>> parse_version("18.1.8")
        (18, 1, 8)
        >>> parse_version("18.1.8git")  # strips non-numeric suffix
        (18, 1, 8)

    Raises:
        ValueError: If version string cannot be parsed
    """
    # Strip common suffixes like "git", "rc1", "+cu128", etc.
    clean = re.match(r"^[\d.]+", version_str.strip())
    if not clean:
        raise ValueError(f"Cannot parse version: {version_str}")

    parts = clean.group().rstrip(".").split(".")
    return tuple(int(p) for p in parts if p)

def format_version(version: Tuple[int, ...]) -> str:
    """
    Format version tuple to string.

    Args:
        version: Tuple of integers like (3, 8), (18, 1, 8), (13,)

    Returns:
        Version string: "3.8", "18.1.8", "13"

    Examples:
        >>> format_version((3, 8))
        '3.8'
        >>> format_version((18, 1, 8))
        '18.1.8'
        >>> format_version((13,))
        '13'
    """
    return ".".join(map(str, version))

# =============================================================================
# REQUIREMENT DATACLASS (Utility Only)
# =============================================================================

@dataclass
class Requirement:
    """
    A version requirement utility class (NOT the source of truth).

    NOTE: This is for utility methods only. Actual version requirements come from
    CMake's DependencyRequirements.cmake and are exported via JSON.

    Attributes:
        name: Human-readable name (e.g., "CMake", "LLVM")
        min_version: Minimum required version tuple, or None for "any version"
        recommended: Recommended version tuple for download URLs (optional)
    """


Requirement

    Returns:
        Version string: "3.8", "18.1.8", "13"

    Examples:
        >>> format_version((3, 8))
        '3.8'
        >>> format_version((18, 1, 8))
        '18.1.8'
        >>> format_version((13,))
        '13'
    """
    return ".".join(map(str, version))

# =============================================================================
# REQUIREMENT DATACLASS (Utility Only)
# =============================================================================

@dataclass
class Requirement:
    """
    A version requirement utility class (NOT the source of truth).

    NOTE: This is for utility methods only. Actual version requirements come from
    CMake's DependencyRequirements.cmake and are exported via JSON.

    Attributes:
        name: Human-readable name (e.g., "CMake", "LLVM")
        min_version: Minimum required version tuple, or None for "any version"
        recommended: Recommended version tuple for download URLs (optional)
    """

    name: str
    min_version: Optional[Tuple[int, ...]]
    recommended: Optional[Tuple[int, ...]] = None

    @property
    def min_str(self) -> str:
        """Minimum version as string: '3.18' or 'any'"""
        if self.min_version is None:
            return "any"
        return format_version(self.min_version)

    @property
    def min_display(self) -> str:
        """Minimum version for display: '3.18+' or 'any version'"""
        if self.min_version is None:
            return "any version"
        return f"{self.min_str}+"

    @property
    def recommended_str(self) -> str:
        """Recommended version as string, falls back to min_str"""
        if self.recommended is None:
            return self.min_str
        return format_version(self.recommended)

    def check(self, detected: Tuple[int, ...]) -> bool:
        """
        Check if detected version meets minimum requirement.

        Args:
            detected: Detected version tuple (e.g., from parse_version)

        Returns:
            True if detected >= min_version (or min_version is None)

        Note:
            Compares only as many parts as min_version specifies.
            So (3, 22, 1) >= (3, 18) compares (3, 22) >= (3, 18) -> True
        """
        if self.min_version is None:
            return True
        # Compare only as many parts as min_version specifies
        return detected[: len(self.min_version)] >= self.min_version

# =============================================================================
# CUDA VERSIONS - For PyTorch wheel URLs
# =============================================================================

# PyTorch wheel CUDA versions currently available (newest first)
CUDA_AVAILABLE = [(13, 1), (13, 0), (12, 8)]

# =============================================================================
# URL GENERATORS
# =============================================================================

def cuda_wheel_suffix(cuda: Tuple[int, int]) -> str:
    """
    Convert CUDA version tuple to PyTorch wheel suffix.

    Examples:


        """
        if self.min_version is None:
            return True
        # Compare only as many parts as min_version specifies
        return detected[: len(self.min_version)] >= self.min_version

# =============================================================================
# CUDA VERSIONS - For PyTorch wheel URLs
# =============================================================================

# PyTorch wheel CUDA versions currently available (newest first)
CUDA_AVAILABLE = [(13, 1), (13, 0), (12, 8)]

# =============================================================================
# URL GENERATORS
# =============================================================================

def cuda_wheel_suffix(cuda: Tuple[int, int]) -> str:
    """
    Convert CUDA version tuple to PyTorch wheel suffix.

    Examples:
        >>> cuda_wheel_suffix((12, 8))
        'cu128'
        >>> cuda_wheel_suffix((13, 1))
        'cu131'
    """
    return f"cu{cuda[0]}{cuda[1]}"

def pytorch_index_url(cuda: Tuple[int, int]) -> str:
    """
    Generate PyTorch wheel index URL for a CUDA version.

    Examples:
        >>> pytorch_index_url((12, 8))
        'https://download.pytorch.org/whl/cu128'
        >>> pytorch_index_url((13, 1))
        'https://download.pytorch.org/whl/cu131'
    """
    return f"https://download.pytorch.org/whl/{cuda_wheel_suffix(cuda)}"

def pytorch_install_instructions(cuda_major: Optional[int] = None) -> str:
    """
    Generate PyTorch installation instructions.

    Args:


# =============================================================================
# URL GENERATORS
# =============================================================================

def cuda_wheel_suffix(cuda: Tuple[int, int]) -> str:
    """
    Convert CUDA version tuple to PyTorch wheel suffix.

    Examples:
        >>> cuda_wheel_suffix((12, 8))
        'cu128'
        >>> cuda_wheel_suffix((13, 1))
        'cu131'
    """
    return f"cu{cuda[0]}{cuda[1]}"

def pytorch_index_url(cuda: Tuple[int, int]) -> str:
    """
    Generate PyTorch wheel index URL for a CUDA version.

    Examples:
        >>> pytorch_index_url((12, 8))
        'https://download.pytorch.org/whl/cu128'
        >>> pytorch_index_url((13, 1))
        'https://download.pytorch.org/whl/cu131'
    """
    return f"https://download.pytorch.org/whl/{cuda_wheel_suffix(cuda)}"

def pytorch_install_instructions(cuda_major: Optional[int] = None) -> str:
    """
    Generate PyTorch installation instructions.

    Args:
        cuda_major: If specified, only show instructions for this CUDA major version.
                   Otherwise show all available versions.

    Returns:
        Multi-line string with pip install commands
    """
    if cuda_major is not None:
        # Filter to matching CUDA major version
        matching = [cuda for cuda in CUDA_AVAILABLE if cuda[0] == cuda_major]
        versions_to_show = matching if matching else CUDA_AVAILABLE
    else:
        versions_to_show = CUDA_AVAILABLE


        'cu128'
        >>> cuda_wheel_suffix((13, 1))
        'cu131'
    """
    return f"cu{cuda[0]}{cuda[1]}"

def pytorch_index_url(cuda: Tuple[int, int]) -> str:
    """
    Generate PyTorch wheel index URL for a CUDA version.

    Examples:
        >>> pytorch_index_url((12, 8))
        'https://download.pytorch.org/whl/cu128'
        >>> pytorch_index_url((13, 1))
        'https://download.pytorch.org/whl/cu131'
    """
    return f"https://download.pytorch.org/whl/{cuda_wheel_suffix(cuda)}"

def pytorch_install_instructions(cuda_major: Optional[int] = None) -> str:
    """
    Generate PyTorch installation instructions.

    Args:
        cuda_major: If specified, only show instructions for this CUDA major version.
                   Otherwise show all available versions.

    Returns:
        Multi-line string with pip install commands
    """
    if cuda_major is not None:
        # Filter to matching CUDA major version
        matching = [cuda for cuda in CUDA_AVAILABLE if cuda[0] == cuda_major]
        versions_to_show = matching if matching else CUDA_AVAILABLE
    else:
        versions_to_show = CUDA_AVAILABLE

    lines = []
    for cuda in versions_to_show:
        lines.append(f"  # For CUDA {format_version(cuda)}:")
        lines.append(f"  pip install torch --index-url {pytorch_index_url(cuda)}")
    return "\n".join(lines)

def llvm_download_url(version: Optional[Tuple[int, ...]] = None) -> str:
    """
    Generate LLVM prebuilt binary download URL.

    Args:
        version: LLVM version tuple. If None, uses (18, 1, 8) as default.

    Returns:
        GitHub release URL for prebuilt binary matching current platform

    Raises:
        NotImplementedError: If platform is not supported

    Example:
        >>> llvm_download_url((18, 1, 8))  # doctest: +SKIP
        'https://github.com/llvm/llvm-project/releases/download/llvmorg-18.1.8/clang+llvm-18.1.8-x86_64-linux-gnu-ubuntu-18.04.tar.xz'
    """
    if version is None:


        cuda_major: If specified, only show instructions for this CUDA major version.
                   Otherwise show all available versions.

    Returns:
        Multi-line string with pip install commands
    """
    if cuda_major is not None:
        # Filter to matching CUDA major version
        matching = [cuda for cuda in CUDA_AVAILABLE if cuda[0] == cuda_major]
        versions_to_show = matching if matching else CUDA_AVAILABLE
    else:
        versions_to_show = CUDA_AVAILABLE

    lines = []
    for cuda in versions_to_show:
        lines.append(f"  # For CUDA {format_version(cuda)}:")
        lines.append(f"  pip install torch --index-url {pytorch_index_url(cuda)}")
    return "\n".join(lines)

def llvm_download_url(version: Optional[Tuple[int, ...]] = None) -> str:
    """
    Generate LLVM prebuilt binary download URL.

    Args:
        version: LLVM version tuple. If None, uses (18, 1, 8) as default.

    Returns:
        GitHub release URL for prebuilt binary matching current platform

    Raises:
        NotImplementedError: If platform is not supported

    Example:
        >>> llvm_download_url((18, 1, 8))  # doctest: +SKIP
        'https://github.com/llvm/llvm-project/releases/download/llvmorg-18.1.8/clang+llvm-18.1.8-x86_64-linux-gnu-ubuntu-18.04.tar.xz'
    """
    if version is None:
        version = (18, 1, 8)

    v = format_version(version)
    machine = platform.machine()

    if machine == "x86_64":
        return (
            f"https://github.com/llvm/llvm-project/releases/download/"
            f"llvmorg-{v}/clang+llvm-{v}-x86_64-linux-gnu-ubuntu-18.04.tar.xz"
        )
    elif machine == "aarch64":
        return (
            f"https://github.com/llvm/llvm-project/releases/download/"
            f"llvmorg-{v}/clang+llvm-{v}-aarch64-linux-gnu.tar.xz"
        )
    elif machine.startswith("arm64"):
        # 64-bit ARM (macOS)
        return (
            f"https://github.com/llvm/llvm-project/releases/download/"
            f"llvmorg-{v}/clang+llvm-{v}-arm64-apple-macos11.tar.xz"
        )
    else:
        raise NotImplementedError(
            f"LLVM prebuilt binaries not available for: {machine}"
        )

def cuda_toolkit_download_url() -> str:
    """
    Return NVIDIA CUDA Toolkit download page URL.

    Returns:
        URL to CUDA downloads page
    """
    return "https://developer.nvidia.com/cuda-downloads"


            f"https://github.com/llvm/llvm-project/releases/download/"
            f"llvmorg-{v}/clang+llvm-{v}-x86_64-linux-gnu-ubuntu-18.04.tar.xz"
        )
    elif machine == "aarch64":
        return (
            f"https://github.com/llvm/llvm-project/releases/download/"
            f"llvmorg-{v}/clang+llvm-{v}-aarch64-linux-gnu.tar.xz"
        )
    elif machine.startswith("arm64"):
        # 64-bit ARM (macOS)
        return (
            f"https://github.com/llvm/llvm-project/releases/download/"
            f"llvmorg-{v}/clang+llvm-{v}-arm64-apple-macos11.tar.xz"
        )
    else:
        raise NotImplementedError(
            f"LLVM prebuilt binaries not available for: {machine}"
        )

def cuda_toolkit_download_url() -> str:
    """
    Return NVIDIA CUDA Toolkit download page URL.

    Returns:
        URL to CUDA downloads page
    """
    return "https://developer.nvidia.com/cuda-downloads"


@dataclass
class Requirement:
    """
    A version requirement utility class (NOT the source of truth).

    NOTE: This is for utility methods only. Actual version requirements come from
    CMake's DependencyRequirements.cmake and are exported via JSON.

    Attributes:
        name: Human-readable name (e.g., "CMake", "LLVM")
        min_version: Minimum required version tuple, or None for "any version"
        recommended: Recommended version tuple for download URLs (optional)
    """

    name: str
    min_version: Optional[Tuple[int, ...]]
    recommended: Optional[Tuple[int, ...]] = None

    @property
    def min_str(self) -> str:
        """Minimum version as string: '3.18' or 'any'"""
        if self.min_version is None:
            return "any"
        return format_version(self.min_version)

    @property
    def min_display(self) -> str:
        """Minimum version for display: '3.18+' or 'any version'"""
        if self.min_version is None:
            return "any version"
        return f"{self.min_str}+"

    @property
    def recommended_str(self) -> str:
        """Recommended version as string, falls back to min_str"""
        if self.recommended is None:
            return self.min_str
        return format_version(self.recommended)

    def check(self, detected: Tuple[int, ...]) -> bool:
        """
        Check if detected version meets minimum requirement.

        Args:


    CMake's DependencyRequirements.cmake and are exported via JSON.

    Attributes:
        name: Human-readable name (e.g., "CMake", "LLVM")
        min_version: Minimum required version tuple, or None for "any version"
        recommended: Recommended version tuple for download URLs (optional)
    """

    name: str
    min_version: Optional[Tuple[int, ...]]
    recommended: Optional[Tuple[int, ...]] = None

    @property
    def min_str(self) -> str:
        """Minimum version as string: '3.18' or 'any'"""
        if self.min_version is None:
            return "any"
        return format_version(self.min_version)

    @property
    def min_display(self) -> str:
        """Minimum version for display: '3.18+' or 'any version'"""
        if self.min_version is None:
            return "any version"
        return f"{self.min_str}+"

    @property
    def recommended_str(self) -> str:
        """Recommended version as string, falls back to min_str"""
        if self.recommended is None:
            return self.min_str
        return format_version(self.recommended)

    def check(self, detected: Tuple[int, ...]) -> bool:
        """
        Check if detected version meets minimum requirement.

        Args:
            detected: Detected version tuple (e.g., from parse_version)

        Returns:
            True if detected >= min_version (or min_version is None)

        Note:
            Compares only as many parts as min_version specifies.


    name: str
    min_version: Optional[Tuple[int, ...]]
    recommended: Optional[Tuple[int, ...]] = None

    @property
    def min_str(self) -> str:
        """Minimum version as string: '3.18' or 'any'"""
        if self.min_version is None:
            return "any"
        return format_version(self.min_version)

    @property
    def min_display(self) -> str:
        """Minimum version for display: '3.18+' or 'any version'"""
        if self.min_version is None:
            return "any version"
        return f"{self.min_str}+"

    @property
    def recommended_str(self) -> str:
        """Recommended version as string, falls back to min_str"""
        if self.recommended is None:
            return self.min_str
        return format_version(self.recommended)

    def check(self, detected: Tuple[int, ...]) -> bool:
        """
        Check if detected version meets minimum requirement.

        Args:
            detected: Detected version tuple (e.g., from parse_version)

        Returns:
            True if detected >= min_version (or min_version is None)

        Note:
            Compares only as many parts as min_version specifies.
            So (3, 22, 1) >= (3, 18) compares (3, 22) >= (3, 18) -> True
        """
        if self.min_version is None:
            return True
        # Compare only as many parts as min_version specifies
        return detected[: len(self.min_version)] >= self.min_version


check

    def min_str(self) -> str:
        """Minimum version as string: '3.18' or 'any'"""
        if self.min_version is None:
            return "any"
        return format_version(self.min_version)

    @property
    def min_display(self) -> str:
        """Minimum version for display: '3.18+' or 'any version'"""
        if self.min_version is None:
            return "any version"
        return f"{self.min_str}+"

    @property
    def recommended_str(self) -> str:
        """Recommended version as string, falls back to min_str"""
        if self.recommended is None:
            return self.min_str
        return format_version(self.recommended)

    def check(self, detected: Tuple[int, ...]) -> bool:
        """
        Check if detected version meets minimum requirement.

        Args:
            detected: Detected version tuple (e.g., from parse_version)

        Returns:
            True if detected >= min_version (or min_version is None)

        Note:
            Compares only as many parts as min_version specifies.
            So (3, 22, 1) >= (3, 18) compares (3, 22) >= (3, 18) -> True
        """
        if self.min_version is None:
            return True
        # Compare only as many parts as min_version specifies
        return detected[: len(self.min_version)] >= self.min_version

# =============================================================================
# CUDA VERSIONS - For PyTorch wheel URLs
# =============================================================================

# PyTorch wheel CUDA versions currently available (newest first)
CUDA_AVAILABLE = [(13, 1), (13, 0), (12, 8)]

# =============================================================================
# URL GENERATORS
# =============================================================================

def cuda_wheel_suffix(cuda: Tuple[int, int]) -> str:
    """
    Convert CUDA version tuple to PyTorch wheel suffix.

    Examples:


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

from typing import Dict
from .base import BooleanRequirement

class GitSubmodulesRequirement(BooleanRequirement):
    """
    Git submodules initialization check.

    CMake variables used:
    - GitSubmodules_FOUND: Whether submodules are initialized
    - NVFUSER_REQUIREMENT_GitSubmodules_STATUS: Validation status
    - NVFUSER_REQUIREMENT_GitSubmodules_OPTIONAL: Whether submodules are optional

    No version checking - simple pass/fail.
    """

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

        Args:
            cmake_vars: Dictionary of all CMake variables
        """
        # Define dependency name and CMake variable names for this requirement
        name = "Submodule status"
        found_var = "GitSubmodules_FOUND"
        status_var = "NVFUSER_REQUIREMENT_GitSubmodules_STATUS"
        optional_var = "NVFUSER_REQUIREMENT_GitSubmodules_OPTIONAL"
        location_var = "NVFUSER_REQUIREMENT_GitSubmodules_LOCATION_VAR"

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

    def generate_help(self, platform_info):
        """
        Generate Git submodules help.

        Args:
            platform_info: Platform detection dict from detect_platform()
        """
        print("Git Submodules Not Initialized")
        print()
        print(
            "Why: nvFuser depends on third-party libraries included as Git submodules."
        )
        print()
        print("Initialize and update Git submodules:")
        print()
        print("  # From the repository root:")
        print("  git submodule update --init --recursive")
        print()
        print("  # Or if you just cloned:")
        print("  git clone --recursive <repository-url>")
        print()


# All rights reserved.
# SPDX-License-Identifier: BSD-3-Clause
"""Git submodules dependency requirement."""

from typing import Dict
from .base import BooleanRequirement

class GitSubmodulesRequirement(BooleanRequirement):
    """
    Git submodules initialization check.

    CMake variables used:
    - GitSubmodules_FOUND: Whether submodules are initialized
    - NVFUSER_REQUIREMENT_GitSubmodules_STATUS: Validation status
    - NVFUSER_REQUIREMENT_GitSubmodules_OPTIONAL: Whether submodules are optional

    No version checking - simple pass/fail.
    """

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

        Args:
            cmake_vars: Dictionary of all CMake variables
        """
        # Define dependency name and CMake variable names for this requirement
        name = "Submodule status"
        found_var = "GitSubmodules_FOUND"
        status_var = "NVFUSER_REQUIREMENT_GitSubmodules_STATUS"
        optional_var = "NVFUSER_REQUIREMENT_GitSubmodules_OPTIONAL"
        location_var = "NVFUSER_REQUIREMENT_GitSubmodules_LOCATION_VAR"

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

    def generate_help(self, platform_info):
        """
        Generate Git submodules help.

        Args:
            platform_info: Platform detection dict from detect_platform()
        """
        print("Git Submodules Not Initialized")
        print()
        print(
            "Why: nvFuser depends on third-party libraries included as Git submodules."
        )
        print()
        print("Initialize and update Git submodules:")
        print()
        print("  # From the repository root:")
        print("  git submodule update --init --recursive")
        print()
        print("  # Or if you just cloned:")


    """

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

        Args:
            cmake_vars: Dictionary of all CMake variables
        """
        # Define dependency name and CMake variable names for this requirement
        name = "Submodule status"
        found_var = "GitSubmodules_FOUND"
        status_var = "NVFUSER_REQUIREMENT_GitSubmodules_STATUS"
        optional_var = "NVFUSER_REQUIREMENT_GitSubmodules_OPTIONAL"
        location_var = "NVFUSER_REQUIREMENT_GitSubmodules_LOCATION_VAR"

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

    def generate_help(self, platform_info):
        """
        Generate Git submodules help.

        Args:
            platform_info: Platform detection dict from detect_platform()
        """
        print("Git Submodules Not Initialized")
        print()
        print(
            "Why: nvFuser depends on third-party libraries included as Git submodules."
        )
        print()
        print("Initialize and update Git submodules:")
        print()
        print("  # From the repository root:")
        print("  git submodule update --init --recursive")
        print()
        print("  # Or if you just cloned:")
        print("  git clone --recursive <repository-url>")
        print()