120 lines
4.8 KiB
Python
120 lines
4.8 KiB
Python
#!/usr/bin/env python3
|
|
import os
|
|
import re
|
|
import sys
|
|
import requests
|
|
from bs4 import BeautifulSoup
|
|
|
|
def generate_nvidia_ids(url, output_dir="."):
|
|
print(f"Fetching NVIDIA chip manifests from: {url}")
|
|
headers = {'User-Agent': 'Mozilla/5.0 (NixOS NVIDIA-detect Tool)'}
|
|
|
|
try:
|
|
response = requests.get(url, headers=headers)
|
|
response.raise_for_status()
|
|
except Exception as e:
|
|
print(f"Execution failed - unable to fetch manifest: {e}", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
soup = BeautifulSoup(response.text, 'html.parser')
|
|
driver_maps = {"current": []}
|
|
current_driver = "current"
|
|
|
|
body = soup.body if soup.body else soup
|
|
|
|
# Linear DOM traversal utilizing structural and literal textual boundaries
|
|
for element in body.descendants:
|
|
# 1. Identify text blocks capable of changing the tracking context
|
|
if element.name in ['h1', 'h2', 'h3', 'h4', 'h5', 'p', 'div']:
|
|
text = element.get_text(" ", strip=True)
|
|
|
|
# Hard reset to stable driver branch when hitting the primary section header
|
|
if "Current NVIDIA GPUs" in text:
|
|
current_driver = "current"
|
|
continue
|
|
|
|
# Switch to legacy branch tracking when encountering driver spec strings
|
|
match = re.search(r'The\s+(\d+)(?:\.\d+)?(?:\.xx)?\s+driver\s+supports', text, re.IGNORECASE)
|
|
if match:
|
|
ver = match.group(1)
|
|
current_driver = f"legacy_{ver}"
|
|
if current_driver == "legacy_470":
|
|
break
|
|
if current_driver not in driver_maps:
|
|
driver_maps[current_driver] = []
|
|
continue
|
|
|
|
# 2. Process data rows inside standard HTML tables
|
|
elif element.name == 'tr':
|
|
cells = [td.get_text(strip=True) for td in element.find_all(['td', 'th'])]
|
|
if len(cells) < 2:
|
|
continue
|
|
|
|
# Evaluate columns looking for the primary 4-digit hex PCI ID
|
|
for cell in cells[1:]:
|
|
cleaned = cell.strip()
|
|
if cleaned.lower().startswith('0x'):
|
|
cleaned = cleaned[2:]
|
|
|
|
words = cleaned.split()
|
|
if not words:
|
|
continue
|
|
|
|
first_word = words[0]
|
|
if re.match(r'^[0-9a-fA-F]{4}$', first_word):
|
|
pci_id = first_word.lower()
|
|
if pci_id not in driver_maps[current_driver]:
|
|
driver_maps[current_driver].append(pci_id)
|
|
break
|
|
|
|
# 3. Fallback processing for older/minimalist pre-formatted text layouts
|
|
elif element.name == 'pre':
|
|
for line in element.get_text().split('\n'):
|
|
line = line.strip()
|
|
if not line:
|
|
continue
|
|
|
|
if "Current NVIDIA GPUs" in line:
|
|
current_driver = "current"
|
|
continue
|
|
|
|
match = re.search(r'The\s+(\d+)(?:\.\d+)?(?:\.xx)?\s+driver\s+supports', line, re.IGNORECASE)
|
|
if match:
|
|
ver = match.group(1)
|
|
current_driver = f"legacy_{ver}"
|
|
if current_driver not in driver_maps:
|
|
driver_maps[current_driver] = []
|
|
continue
|
|
|
|
parts = re.split(r'[,.]', line)
|
|
if len(parts) >= 2:
|
|
val = parts[1].strip()
|
|
if val.lower().startswith('0x'):
|
|
val = val[2:]
|
|
words = val.split()
|
|
if words:
|
|
first_word = words[0]
|
|
if re.match(r'^[0-9a-fA-F]{4}$', first_word):
|
|
pci_id = first_word.lower()
|
|
if pci_id not in driver_maps[current_driver]:
|
|
driver_maps[current_driver].append(pci_id)
|
|
|
|
# Clean file generation
|
|
os.makedirs(output_dir, exist_ok=True)
|
|
for driver, ids in driver_maps.items():
|
|
if not ids:
|
|
continue
|
|
filename = os.path.join(output_dir, f"{driver}.ids")
|
|
with open(filename, "w") as f:
|
|
for pci_id in sorted(ids):
|
|
f.write(f"{pci_id}\n")
|
|
print(f"Generated {filename} containing {len(ids)} mapped PCI IDs.")
|
|
|
|
if __name__ == "__main__":
|
|
version = "595.84"
|
|
if len(sys.argv) > 1:
|
|
version = sys.argv[1]
|
|
target_url = f"https://download.nvidia.com/XFree86/Linux-x86_64/{version}/README/supportedchips.html"
|
|
else:
|
|
print("Please provide the driver version under following format : xxx.xx, for example : 595.84")
|
|
generate_nvidia_ids(target_url) |