#!/usr/bin/env python3
# This script is used to evaluate Nixpkgs and store the relationships between
# derivations and their input derivations in a directed acyclic graph (DAG).
# The DAG is represented using an adjacency list and saved in a SQLite db
import re
import subprocess
import json
import sqlite3
from typing import Set
class drv:
pname: str
input_drvs: Set[str] = set()
cursor: sqlite3.Cursor
def __init__(self, drv_string: str, cursor: sqlite3.Cursor) -> None:
self.cursor = cursor
j = json.loads(drv_string)
if 'drvPath' not in j:
raise TypeError
pname = self.pname_from_drv_path(j['drvPath'])
print(pname)
if pname is None:
raise TypeError
self.pname = pname
for input_drv in j['inputDrvs']:
pname = self.pname_from_drv_path(input_drv)
if pname is not None:
self.input_drvs.add(pname)
def db_push(self):
parrent_id = self.rowid_from_pname(self.cursor, self.pname)
for input_drv in self.input_drvs:
child_id = self.rowid_from_pname(self.cursor, input_drv)
self.cursor.execute("""
INSERT INTO edges (start, end)
VALUES (?, ?)
""", (parrent_id, child_id))
@staticmethod
def rowid_from_pname(cursor: sqlite3.Cursor, pname: str) -> int:
s = cursor.execute("""
SELECT pnames.ROWID FROM pnames
WHERE pnames.pname = ?
""", (pname,))
id = s.fetchone()
if id:
return id[0]
s = cursor.execute("""
INSERT INTO pnames (pname)
VALUES (?)
""", (pname,))
if s.lastrowid is None:
raise TypeError
return s.lastrowid
@staticmethod
def pname_from_drv_path(drv_path: str) -> str | None:
f = open(drv_path, 'r')
drv_string = f.readline()
match = re.search('"pname","([^"]+)', drv_string)
if match is not None:
return match.group(1)
if __name__ == '__main__':
cmd = [
'nix-eval-jobs',
'--flake',
'github:nixos/nixpkgs#legacyPackages.x86_64-linux'
]
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL)
if proc.stdout is None:
raise TypeError
con = sqlite3.connect('nixpkgs_dag.db')
cur = con.cursor()
cur.execute("""
CREATE TABLE IF NOT EXISTS pnames (
pname TEXT NOT NULL UNIQUE
)
""")
cur.execute("""
CREATE TABLE IF NOT EXISTS edges (
start INTEGER NOT NULL,
end NOT NULL,
UNIQUE(start, end) ON CONFLICT REPLACE
)
""")
for line in proc.stdout:
try:
d = drv(line.decode('utf-8'), cur)
d.db_push()
except Exception as e:
print(str(e))
con.commit()
con.close()