dwrswr/cpp-module-06-guide
0
1# -*- coding: utf-8 -*-2"""Build index.html from tools/index.src.html, injecting the REAL cpp06 sources.3 4Placeholders: {{CODE relative/path|dom-id|flags}}5 flags: comma list, any of tall plain6Language is inferred from the file name. Because the code blocks are read from7../cpp06 at build time, the published guide can never drift from the project.8"""9import html10import os11import shutil12import sys13 14HERE = os.path.dirname(os.path.abspath(__file__))15GUIDE = os.path.dirname(HERE)16SRC = os.path.abspath(os.path.join(GUIDE, os.pardir, "cpp06"))17TMPL = os.path.join(HERE, "index.src.html")18OUT = os.path.join(GUIDE, "index.html")19MIRROR = os.path.join(GUIDE, "code")20 21LANG = {".cpp": ("cpp", "C++98"), ".hpp": ("cpp", "C++98"), ".sh": ("sh", "shell")}22 23 24def lang_of(path):25 base = os.path.basename(path)26 if base == "Makefile":27 return "make", "make"28 return LANG.get(os.path.splitext(base)[1], ("cpp", "C++98"))29 30 31def block(spec):32 parts = [p.strip() for p in spec.split("|")]33 rel = parts[0]34 dom = parts[1] if len(parts) > 1 and parts[1] else "code-" + rel.replace("/", "-").replace(".", "-")35 flags = [f for f in (parts[2].split(",") if len(parts) > 2 and parts[2] else []) if f]36 full = os.path.join(SRC, rel.replace("/", os.sep))37 with open(full, "r", encoding="utf-8", newline="") as fh:38 text = fh.read().replace("\r\n", "\n").rstrip("\n")39 lang, tag = lang_of(rel)40 n = text.count("\n") + 141 cls = "code" + (" tall" if "tall" in flags else "") + (" plain" if "plain" in flags else "")42 return (43 '<div class="%s" id="%s">\n'44 ' <div class="code-head"><span class="dot" aria-hidden="true"></span>'45 '<span class="fn">cpp06/%s</span>'46 '<span class="tag">%s · %d lines</span>'47 '<button class="copy" type="button">copy</button></div>\n'48 ' <div class="code-body"><pre data-lang="%s">%s</pre></div>\n'49 "</div>"50 ) % (cls, dom, rel, tag, n, lang, html.escape(text, quote=False))51 52 53def main():54 with open(TMPL, "r", encoding="utf-8") as fh:55 page = fh.read()56 out, i, used = [], 0, []57 while True:58 a = page.find("{{CODE ", i)59 if a < 0:60 out.append(page[i:])61 break62 b = page.find("}}", a)63 if b < 0:64 raise SystemExit("unterminated placeholder at %d" % a)65 out.append(page[i:a])66 spec = page[a + 7:b]67 out.append(block(spec))68 used.append(spec.split("|")[0].strip())69 i = b + 270 with open(OUT, "w", encoding="utf-8", newline="\n") as fh:71 fh.write("".join(out))72 73 if os.path.isdir(MIRROR):74 shutil.rmtree(MIRROR)75 shutil.copytree(SRC, MIRROR)76 for root, _dirs, files in os.walk(MIRROR):77 for f in files:78 p = os.path.join(root, f)79 with open(p, "rb") as fh:80 data = fh.read()81 with open(p, "wb") as fh:82 fh.write(data.replace(b"\r\n", b"\n"))83 84 every = []85 for root, _dirs, files in os.walk(SRC):86 for f in files:87 every.append(os.path.relpath(os.path.join(root, f), SRC).replace(os.sep, "/"))88 missing = sorted(set(every) - set(used))89 sys.stdout.write("built %s (%d bytes)\n" % (OUT, os.path.getsize(OUT)))90 sys.stdout.write("code blocks injected: %d of %d project files\n" % (len(set(used)), len(every)))91 if missing:92 sys.stdout.write("NOT shown in the guide: %s\n" % ", ".join(missing))93 94 95if __name__ == "__main__":96 main()97 