Skip to content

Commit ebaa8b2

Browse files
committed
generate alectryon rules for dune
Signed-off-by: Ali Caglayan <[email protected]>
1 parent d5d0e63 commit ebaa8b2

File tree

7 files changed

+368
-8
lines changed

7 files changed

+368
-8
lines changed

.github/workflows/ci.yml

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ jobs:
159159
# - coqchk: Runs coqchk
160160
# - install: Tests install target
161161

162-
# alectryon doc job (using Nix for reproducible builds)
162+
# alectryon doc job (using Nix + dune for reproducible builds)
163163
doc-alectryon:
164164
runs-on: ubuntu-latest
165165
steps:
@@ -171,14 +171,11 @@ jobs:
171171
with:
172172
primary-key: nix-${{ runner.os }}-${{ hashFiles('flake.lock') }}
173173
restore-prefixes-first-match: nix-${{ runner.os }}-
174-
- name: add problem matchers
174+
- name: Build alectryon docs with dune
175175
run: |
176-
echo "::add-matcher::etc/coq-scripts/github/alectryon-error.json"
177-
- name: Build and run alectryon
178-
run: |
179-
nix develop .#coq_9_0 -c make alectryon ALECTRYON_EXTRAFLAGS=--traceback
176+
nix develop .#coq_9_0 -c dune build @alectryon
180177
- name: tar alectryon artifact
181-
run: tar -cf alectryon-html.tar alectryon-html
178+
run: tar -cf alectryon-html.tar -C _build/default alectryon-html
182179
- name: upload alectryon artifact
183180
uses: actions/upload-artifact@v4
184181
with:

dune

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,23 @@
4848
(alias_rec theories/all)
4949
_CoqProject))
5050

51+
; Alectryon documentation
52+
; Run with: dune build @alectryon
53+
54+
(rule
55+
(target alectryon_rules.sexp)
56+
(deps
57+
(glob_files theories/*.v)
58+
(glob_files theories/**/*.v)
59+
(glob_files contrib/*.v))
60+
(action
61+
(with-stdout-to %{target}
62+
(chdir %{project_root}
63+
(run etc/gen-alectryon-rules/gen_alectryon_rules.exe)))))
64+
65+
(subdir alectryon-html
66+
(dynamic_include ../alectryon_rules.sexp))
67+
5168
; Tags for emacs
5269

5370
(rule

dune-project

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
(lang dune 3.13)
1+
(lang dune 3.14)
22

33
(using coq 0.8)
4+
(using directory-targets 0.1)
45

56
(name coq-hott)
67

etc/gen-alectryon-rules/README.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# Alectryon Rules Generator
2+
3+
This tool generates dune rules for parallel Alectryon documentation processing.
4+
5+
## Overview
6+
7+
The generator creates one dune rule per `.v` file in `theories/` and
8+
`contrib/`. Each rule:
9+
10+
1. Runs `fcc` (Flèche Coq Compiler from coq-lsp) with the goaldump plugin to
11+
extract proof goals
12+
2. Converts the goaldump JSON to Alectryon's JSON format using
13+
`goaldump-to-alectryon.py` (in this directory)
14+
3. Runs Alectryon with the `coq.io.json` frontend to produce HTML
15+
16+
## Why fcc instead of coq-lsp?
17+
18+
Alectryon normally uses coq-lsp to extract proof states, but this is slow
19+
because coq-lsp is designed for interactive editing with incremental
20+
compilation. For batch documentation generation, `fcc` is much faster as it's
21+
optimized for single-pass compilation.
22+
23+
alectryon will normally ask coq-lsp for each goal separately which takes way
24+
too long. By getting fcc (the Coq compiler based on internals of coq-lsp) to
25+
dump all the goals, we can process them as something alectryon can understand.
26+
27+
## Generated Files
28+
29+
For each `.v` file, the rule produces:
30+
- `<name>.html` - The Alectryon HTML documentation
31+
- `<name>.v.fcc.log` - Log output from fcc (useful for debugging)
32+
33+
Files are output to `alectryon-html/` with flattened names (e.g.,
34+
`theories/WildCat/Core.v` becomes `theories.WildCat.Core.html`).
35+
36+
## Usage
37+
38+
```bash
39+
# Build all documentation
40+
dune build @alectryon
41+
42+
# Build documentation for a single file
43+
dune build alectryon-html/theories.WildCat.Core.html
44+
```
45+
46+
## Dependencies
47+
48+
- `fcc` from coq-lsp with the goaldump plugin (`coq-lsp.plugin.goaldump`)
49+
- Python 3 with the Alectryon package (via `etc/alectryon/`)
50+
- `goaldump-to-alectryon.py` converter script (in this directory)
51+
52+
## How It Works
53+
54+
The rules are generated dynamically using dune's `dynamic_include` feature:
55+
56+
1. `gen_alectryon_rules.exe` scans `theories/` and `contrib/` for `.v` files
57+
2. It outputs dune rules in S-expression format to `alectryon_rules.sexp`
58+
3. The main `dune` file includes these rules via `(dynamic_include
59+
../alectryon_rules.sexp)` in the `alectryon-html` subdirectory
60+
61+
Each rule uses `(sandbox always)` to ensure parallel builds don't interfere
62+
with each other, since `fcc` writes intermediate files next to the source.
63+
64+
## fcc Exit Codes
65+
66+
Note: `fcc` may return exit code 1 even on successful compilation (due to "file
67+
not in workspace" warnings). The rules use `(with-accepted-exit-codes (or 0 1)
68+
...)` to handle this.

etc/gen-alectryon-rules/dune

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
(executable
2+
(name gen_alectryon_rules))
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
(* Generate dune rules for parallel alectryon processing using fcc *)
2+
(* Rules are included from alectryon-html subdir, so paths use ../ prefix *)
3+
4+
let find_v_files dirs =
5+
let files = ref [] in
6+
let rec walk dir =
7+
let entries = Sys.readdir dir in
8+
Array.iter (fun entry ->
9+
let path = Filename.concat dir entry in
10+
if Sys.is_directory path then
11+
walk path
12+
else if Filename.check_suffix entry ".v" then
13+
files := path :: !files
14+
) entries
15+
in
16+
List.iter (fun dir -> if Sys.file_exists dir then walk dir) dirs;
17+
List.sort String.compare !files
18+
19+
let v_to_module path =
20+
(* theories/Foo/Bar.v -> HoTT.Foo.Bar
21+
contrib/Foo.v -> HoTT.Contrib.Foo *)
22+
let base = Filename.chop_suffix path ".v" in
23+
let parts = String.split_on_char '/' base in
24+
match parts with
25+
| "theories" :: rest -> "HoTT." ^ String.concat "." rest
26+
| "contrib" :: rest -> "HoTT.Contrib." ^ String.concat "." rest
27+
| _ -> String.concat "." parts
28+
29+
let v_to_html path =
30+
Printf.sprintf "%s.html" (v_to_module path)
31+
32+
let print_rule vfile =
33+
let html_target = v_to_html vfile in
34+
let alectryon_json = vfile ^ ".alectryon.json" in
35+
Printf.printf "\n(rule\n";
36+
Printf.printf " (target %s)\n" html_target;
37+
Printf.printf " (deps\n";
38+
Printf.printf " (sandbox always)\n";
39+
Printf.printf " ../%s\n" vfile;
40+
Printf.printf " ../_CoqProject\n";
41+
Printf.printf " (glob_files_rec ../theories/*.vo)\n";
42+
Printf.printf " (glob_files_rec ../contrib/*.vo)\n";
43+
Printf.printf " ../etc/gen-alectryon-rules/goaldump-to-alectryon.py\n";
44+
Printf.printf " (source_tree ../etc/alectryon))\n";
45+
let log_target = v_to_module vfile ^ ".v.fcc.log" in
46+
Printf.printf " (action\n";
47+
Printf.printf " (chdir ..\n";
48+
Printf.printf " (progn\n";
49+
Printf.printf " (with-outputs-to alectryon-html/%s (with-accepted-exit-codes (or 0 1) (run fcc --root=. --no_vo --plugin=coq-lsp.plugin.goaldump %s)))\n" log_target vfile;
50+
Printf.printf " (run python3 etc/gen-alectryon-rules/goaldump-to-alectryon.py %s -o %s)\n" vfile alectryon_json;
51+
Printf.printf " (run python3 etc/alectryon/alectryon.py --frontend coq.io.json %s --backend webpage -o alectryon-html/%s)))))\n" alectryon_json html_target
52+
53+
let () =
54+
let dirs = ["theories"; "contrib"] in
55+
let files = find_v_files dirs in
56+
57+
(* Print individual rules *)
58+
List.iter print_rule files;
59+
60+
(* Print umbrella alias *)
61+
Printf.printf "\n(alias\n (name alectryon)\n (deps\n";
62+
List.iter (fun vfile -> Printf.printf " %s\n" (v_to_html vfile)) files;
63+
Printf.printf "))\n"

0 commit comments

Comments
 (0)