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
108
|
#!/usr/bin/env python3
import re
import fileinput
import subprocess
import json
import os
from typing import Set, Iterable
def get_nix_packages() -> Set[str]:
res = subprocess.run(
[
"nix",
"--experimental-features",
"nix-command",
"eval",
"--json",
"-f",
"<nixpkgs>",
"texlive",
"--apply",
"builtins.attrNames",
],
check=True,
text=True,
stdout=subprocess.PIPE,
)
return set(json.loads(res.stdout))
def get_packages(line: str) -> Set[str]:
match = re.match(r"\\(?:usepackage|RequirePackage).*{([^}]+)}", line)
if not match:
return set()
args = match.group(1)
packages = set()
for arg in args.split(","):
packages.add(arg.strip())
return packages
def _collect_deps(
working_set: Set[str], done: Set[str], all_packages: Set[str]
) -> None:
package = working_set.pop()
done.add(package)
cmd = ["nix-build", "--no-out-link", "<nixpkgs>", "-A", f"texlive.{package}.pkgs"]
paths = subprocess.run(cmd, check=True, text=True, stdout=subprocess.PIPE)
for dir in paths.stdout.split("\n"):
path = os.path.join(dir, "tex")
for root, _, files in os.walk(path):
for fname in files:
path = os.path.join(root, fname)
with open(path, "rb") as f:
for line in f:
packages = get_packages(line.decode("utf-8", errors="ignore"))
working_set |= all_packages & (packages - done)
def collect_deps(packages: Set[str], all_packages: Set[str]) -> Set[str]:
working_set: Set[str] = set(packages)
done: Set[str] = set()
while working_set:
_collect_deps(working_set, done, all_packages)
return done
def write_tex_env(dir: str, packages: Set[str]) -> str:
name = os.path.join(dir, "tex-env.nix")
with open(name, "w") as f:
f.write(
"""# Generated with tex2nix 0.0.0
{ texlive, extraTexPackages ? {} }:
(texlive.combine ({
inherit (texlive) scheme-small;
"""
)
for package in packages:
f.write(f' "{package}" = texlive."{package}";\n')
f.write(
"""
} // extraTexPackages))
"""
)
print("wrote tex-env.nix...")
return name
def extract_dependencies(lines: Iterable[str]) -> Set[str]:
packages = set()
for line in lines:
packages |= get_packages(line)
all_packages = get_nix_packages()
nix_packages = packages & all_packages
return collect_deps(nix_packages, all_packages)
def main() -> None:
packages = extract_dependencies(fileinput.input())
write_tex_env(os.getcwd(), packages)
if __name__ == "__main__":
main()
|