aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authortv <tv@krebsco.de>2021-12-08 01:05:35 +0100
committertv <tv@krebsco.de>2021-12-08 03:15:44 +0100
commitc934073436f1b19289634ad1abda6d40d26afb14 (patch)
tree0c02f31e599443390dd61692079c30e31fa917bc
import tex2nix bits
From https://github.com/Mic92/tex2nix commit 9379d49
-rw-r--r--README.md68
-rw-r--r--tex2nix/__init__.py108
2 files changed, 176 insertions, 0 deletions
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..1072dcf
--- /dev/null
+++ b/README.md
@@ -0,0 +1,68 @@
+# tex2nix
+
+Generate Texlive environment containing all dependencies for your document
+rather than downloading gigabytes of texlive packages.
+
+## Installation
+
+With stable nix you can do:
+
+``` console
+nix-build && ./result/bin/tex2nix
+```
+
+If you use flakes put the following in your inputs
+
+```nix
+{
+ inputs.tex2nix.url = "github:Mic92/tex2nix";
+ inputs.tex2nix.inputs.utils.follows = "nixpkgs";
+}
+```
+
+or just do:
+
+```console
+$ nix run github:Mic92/tex2nix
+```
+
+
+## USAGE
+
+```console
+$ tex2nix main.tex
+wrote tex-env.nix
+$ cat tex-env.nix
+# Generated with tex2nix 0.0.0
+{ texlive }:
+(texlive.combine {
+ inherit (texlive) scheme-small;
+ "varwidth" = texlive."varwidth";
+ "tabu" = texlive."tabu";
+
+})
+```
+
+tex2nix does not follow `\input` or `\include`. However you can specify multiple
+texfiles as input
+
+```console
+$ tex2nix *.tex
+```
+
+The resulting expression can be imported like this to in your document directory:
+
+```nix
+# shell.nix
+with import <nixpkgs> {};
+mkShell {
+ buildInputs = [ (pkgs.callPackage ./tex-env.nix {}) ];
+}
+```
+
+``` console
+$ nix-shell shell.nix
+nix-shell> pdflatex --version
+pdfTeX 3.14159265-2.6-1.40.21 (TeX Live 2020/NixOS.org)
+...
+```
diff --git a/tex2nix/__init__.py b/tex2nix/__init__.py
new file mode 100644
index 0000000..252895a
--- /dev/null
+++ b/tex2nix/__init__.py
@@ -0,0 +1,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()