aboutsummaryrefslogtreecommitdiff
path: root/bin/nixpkgs_dag.py
blob: 7b8adea53ed493a737ab729dc859ea6b538280fb (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
#!/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()