リポジトリ / emssr / independent_common_compiler
r2
| r2 emssr | 1 | # -*- coding: utf-8 -*- |
| r2 emssr | 2 | """このフォルダだけで動くようにファイルを揃える。標準機能だけで書く。 |
| r2 emssr | 3 | |
| r2 emssr | 4 | python setup.py runtime/ を揃え、Pyodide をこのフォルダへコピーする |
| r2 emssr | 5 | python setup.py --runtime runtime/ だけ |
| r2 emssr | 6 | python setup.py --vendor Pyodide と web の library だけ |
| r2 emssr | 7 | python setup.py --version 314.0.7 版を指定する |
| r2 emssr | 8 | python setup.py --dist <DIR> 配布用フォルダを作る |
| r2 emssr | 9 | |
| r2 emssr | 10 | 揃うもの。 |
| r2 emssr | 11 | |
| r2 emssr | 12 | runtime/ lane64 の実行台と commonc の image。server.py がここから配信する |
| r2 emssr | 13 | vendor/pyodide/ ブラウザの中の CPython。約 15 MB |
| r2 emssr | 14 | vendor/web/ marked(Markdown)と KaTeX(数式)。約 0.6 MB |
| r2 emssr | 15 | |
| r2 emssr | 16 | Pyodide の版は npm の registry へ訊く。CDN の `full/` には wheel も揃っているので、 |
| r2 emssr | 17 | そこから四本と numpy の wheel を取る。numpy は束ねの道(vcpu_vec)が使う。 |
| r2 emssr | 18 | """ |
| r2 emssr | 19 | |
| r2 emssr | 20 | import json |
| r2 emssr | 21 | import os |
| r2 emssr | 22 | import shutil |
| r2 emssr | 23 | import sys |
| r2 emssr | 24 | import urllib.request |
| r2 emssr | 25 | |
| r2 emssr | 26 | HERE = os.path.dirname(os.path.abspath(__file__)) |
| r2 emssr | 27 | RUNTIME = os.path.join(HERE, "runtime") |
| r2 emssr | 28 | VENDOR = os.path.join(HERE, "vendor", "pyodide") |
| r2 emssr | 29 | WEB = os.path.join(HERE, "vendor", "web") |
| r2 emssr | 30 | ASSETS = os.path.join(HERE, "assets") |
| r2 emssr | 31 | |
| r2 emssr | 32 | REGISTRY = "https://registry.npmjs.org/pyodide/latest" |
| r2 emssr | 33 | CDN = "https://cdn.jsdelivr.net/pyodide/v%s/full/" |
| r2 emssr | 34 | CORE = ("pyodide.js", "pyodide.mjs", "pyodide.asm.mjs", "pyodide.asm.wasm", |
| r2 emssr | 35 | "python_stdlib.zip", "pyodide-lock.json") |
| r2 emssr | 36 | |
| r2 emssr | 37 | # runtime/ に置く名前と、repository での在り処 |
| r2 emssr | 38 | SOURCES = ( |
| r2 emssr | 39 | ("vcpu.py", "continuous_republic/common_systems/Python_vCPU/vcpu.py"), |
| r2 emssr | 40 | ("vcpu_vec.py", "continuous_republic/common_systems/Python_vCPU/vcpu_vec.py"), |
| r2 emssr | 41 | ("lex.py", "continuous_republic/common_systems/Python_vCPU/lex.py"), |
| r2 emssr | 42 | ("toolchain.py", "continuous_republic/common_systems/Python_vCPU/toolchain.py"), |
| r2 emssr | 43 | ("commonc_lane64.bin", "builds/commonc/commonc_lane64.bin"), |
| r2 emssr | 44 | ("joinrank.dic", "continuous_republic/common_apps/joinrank/joinrank/joinrank.dic"), |
| r2 emssr | 45 | ) |
| r2 emssr | 46 | |
| r2 emssr | 47 | |
| r2 emssr | 48 | def root(): |
| r2 emssr | 49 | return os.path.abspath(os.path.join(HERE, os.pardir, os.pardir, os.pardir)) |
| r2 emssr | 50 | |
| r2 emssr | 51 | |
| r2 emssr | 52 | def get(url, path): |
| r2 emssr | 53 | print(" %s" % os.path.basename(path)) |
| r2 emssr | 54 | with urllib.request.urlopen(url) as r, open(path, "wb") as f: |
| r2 emssr | 55 | shutil.copyfileobj(r, f) |
| r2 emssr | 56 | |
| r2 emssr | 57 | |
| r2 emssr | 58 | # ===== runtime ===== |
| r2 emssr | 59 | |
| r2 emssr | 60 | def runtime(): |
| r2 emssr | 61 | os.path.isdir(RUNTIME) or os.makedirs(RUNTIME) |
| r2 emssr | 62 | R = root() |
| r2 emssr | 63 | miss = [] |
| r2 emssr | 64 | for name, rel in SOURCES: |
| r2 emssr | 65 | src = os.path.join(R, rel.replace("/", os.sep)) |
| r2 emssr | 66 | if os.path.exists(src): |
| r2 emssr | 67 | shutil.copyfile(src, os.path.join(RUNTIME, name)) |
| r2 emssr | 68 | print(" %s" % name) |
| r2 emssr | 69 | elif not os.path.exists(os.path.join(RUNTIME, name)): |
| r2 emssr | 70 | miss.append(rel) |
| r2 emssr | 71 | for name in ("joinrank.bin", "anf.bin"): |
| r2 emssr | 72 | dst = os.path.join(RUNTIME, name) |
| r2 emssr | 73 | if os.path.exists(dst): |
| r2 emssr | 74 | continue |
| r2 emssr | 75 | built = os.path.join(R, "continuous_republic", "common_apps", "joinrank", |
| r2 emssr | 76 | "build", name) |
| r2 emssr | 77 | if os.path.exists(built): |
| r2 emssr | 78 | shutil.copyfile(built, dst) |
| r2 emssr | 79 | print(" %s" % name) |
| r2 emssr | 80 | else: |
| r2 emssr | 81 | miss.append("%s(common_apps/joinrank/build.py で作る)" % name) |
| r2 emssr | 82 | if miss: |
| r2 emssr | 83 | print("揃わなかったもの:") |
| r2 emssr | 84 | for m in miss: |
| r2 emssr | 85 | print(" %s" % m) |
| r2 emssr | 86 | return 1 |
| r2 emssr | 87 | return 0 |
| r2 emssr | 88 | |
| r2 emssr | 89 | |
| r2 emssr | 90 | # ===== Pyodide ===== |
| r2 emssr | 91 | |
| r2 emssr | 92 | def vendor(version=None): |
| r2 emssr | 93 | if version is None: |
| r2 emssr | 94 | with urllib.request.urlopen(REGISTRY) as r: |
| r2 emssr | 95 | version = json.load(r)["version"] |
| r2 emssr | 96 | base = CDN % version |
| r2 emssr | 97 | os.path.isdir(VENDOR) or os.makedirs(VENDOR) |
| r2 emssr | 98 | print("Pyodide %s" % version) |
| r2 emssr | 99 | for n in CORE: |
| r2 emssr | 100 | get(base + n, os.path.join(VENDOR, n)) |
| r2 emssr | 101 | lock = json.load(open(os.path.join(VENDOR, "pyodide-lock.json"), encoding="utf-8")) |
| r2 emssr | 102 | wheel = lock["packages"]["numpy"]["file_name"] |
| r2 emssr | 103 | get(base + wheel, os.path.join(VENDOR, wheel)) |
| r2 emssr | 104 | open(os.path.join(VENDOR, "version.txt"), "w").write(version) |
| r2 emssr | 105 | return 0 |
| r2 emssr | 106 | |
| r2 emssr | 107 | |
| r2 emssr | 108 | # ===== web の library ===== |
| r2 emssr | 109 | # |
| r2 emssr | 110 | # notebook の説明セルは Markdown と LaTeX で書く。描くのは browser なので、 |
| r2 emssr | 111 | # marked と KaTeX をこのフォルダへ置く。KaTeX は font を伴う。 |
| r2 emssr | 112 | |
| r2 emssr | 113 | MARKED = "https://cdn.jsdelivr.net/npm/marked@%s/lib/marked.umd.js" |
| r2 emssr | 114 | KATEX = "https://cdn.jsdelivr.net/npm/katex@%s/dist/%s" |
| r2 emssr | 115 | KATEX_FONTS = ( |
| r2 emssr | 116 | "KaTeX_AMS-Regular", "KaTeX_Caligraphic-Bold", "KaTeX_Caligraphic-Regular", |
| r2 emssr | 117 | "KaTeX_Fraktur-Bold", "KaTeX_Fraktur-Regular", "KaTeX_Main-Bold", |
| r2 emssr | 118 | "KaTeX_Main-BoldItalic", "KaTeX_Main-Italic", "KaTeX_Main-Regular", |
| r2 emssr | 119 | "KaTeX_Math-BoldItalic", "KaTeX_Math-Italic", "KaTeX_SansSerif-Bold", |
| r2 emssr | 120 | "KaTeX_SansSerif-Italic", "KaTeX_SansSerif-Regular", "KaTeX_Script-Regular", |
| r2 emssr | 121 | "KaTeX_Size1-Regular", "KaTeX_Size2-Regular", "KaTeX_Size3-Regular", |
| r2 emssr | 122 | "KaTeX_Size4-Regular", "KaTeX_Typewriter-Regular", |
| r2 emssr | 123 | ) |
| r2 emssr | 124 | |
| r2 emssr | 125 | |
| r2 emssr | 126 | def latest(name): |
| r2 emssr | 127 | with urllib.request.urlopen("https://registry.npmjs.org/%s/latest" % name) as r: |
| r2 emssr | 128 | return json.load(r)["version"] |
| r2 emssr | 129 | |
| r2 emssr | 130 | |
| r2 emssr | 131 | def web(marked_v=None, katex_v=None): |
| r2 emssr | 132 | marked_v = marked_v or latest("marked") |
| r2 emssr | 133 | katex_v = katex_v or latest("katex") |
| r2 emssr | 134 | os.path.isdir(os.path.join(WEB, "fonts")) or os.makedirs(os.path.join(WEB, "fonts")) |
| r2 emssr | 135 | print("marked %s / KaTeX %s" % (marked_v, katex_v)) |
| r2 emssr | 136 | get(MARKED % marked_v, os.path.join(WEB, "marked.umd.js")) |
| r2 emssr | 137 | for n in ("katex.min.js", "katex.min.css"): |
| r2 emssr | 138 | get(KATEX % (katex_v, n), os.path.join(WEB, n)) |
| r2 emssr | 139 | get(KATEX % (katex_v, "contrib/auto-render.min.js"), |
| r2 emssr | 140 | os.path.join(WEB, "auto-render.min.js")) |
| r2 emssr | 141 | for f in KATEX_FONTS: |
| r2 emssr | 142 | get(KATEX % (katex_v, "fonts/" + f + ".woff2"), |
| r2 emssr | 143 | os.path.join(WEB, "fonts", f + ".woff2")) |
| r2 emssr | 144 | open(os.path.join(WEB, "version.txt"), "w").write( |
| r2 emssr | 145 | "marked %s\nkatex %s\n" % (marked_v, katex_v)) |
| r2 emssr | 146 | return 0 |
| r2 emssr | 147 | |
| r2 emssr | 148 | |
| r2 emssr | 149 | # ===== 配布用フォルダ ===== |
| r2 emssr | 150 | # |
| r2 emssr | 151 | # repository の外へ出しても動く形にまとめる。runtime/ と vendor/ を抱えるので、 |
| r2 emssr | 152 | # 受け取った側は展開して server.py を動かすだけでよい。 |
| r2 emssr | 153 | # |
| r2 emssr | 154 | # 例題の .cmn は tools/independent_compile_test の examples をそのまま使う。 |
| r2 emssr | 155 | # セルへドロップして実行できる。 |
| r2 emssr | 156 | |
| r2 emssr | 157 | APP = ("server.py", "setup.py", "test_notebook.py", "README.md") |
| r2 emssr | 158 | TREES = ("kernel", "web", "notebooks", "vscode", "runtime", "vendor", "assets") |
| r2 emssr | 159 | EXAMPLES = "tools/independent_compile_test/examples" |
| r2 emssr | 160 | |
| r2 emssr | 161 | |
| r2 emssr | 162 | |
| r2 emssr | 163 | def dist(dst): |
| r2 emssr | 164 | if runtime(): |
| r2 emssr | 165 | return 1 |
| r2 emssr | 166 | if not os.path.isdir(VENDOR): |
| r2 emssr | 167 | print("vendor/ がありません。先に python setup.py --vendor を実行してください") |
| r2 emssr | 168 | return 1 |
| r2 emssr | 169 | os.path.isdir(dst) or os.makedirs(dst) |
| r2 emssr | 170 | for n in APP: |
| r2 emssr | 171 | shutil.copyfile(os.path.join(HERE, n), os.path.join(dst, n)) |
| r2 emssr | 172 | for n in TREES: |
| r2 emssr | 173 | src = os.path.join(HERE, n) |
| r2 emssr | 174 | if not os.path.isdir(src): |
| r2 emssr | 175 | continue |
| r2 emssr | 176 | shutil.copytree(src, os.path.join(dst, n), dirs_exist_ok=True, |
| r2 emssr | 177 | ignore=shutil.ignore_patterns("__pycache__", "*.pyc", "_cell.dic")) |
| r2 emssr | 178 | ex = os.path.join(root(), *EXAMPLES.split("/")) |
| r2 emssr | 179 | if os.path.isdir(ex): |
| r2 emssr | 180 | shutil.copytree(ex, os.path.join(dst, "examples"), dirs_exist_ok=True, |
| r2 emssr | 181 | ignore=shutil.ignore_patterns("__pycache__", "*.pyc")) |
| r2 emssr | 182 | open(os.path.join(dst, "start.ps1"), "w", encoding="utf-8", newline="\r\n").write( |
| r2 emssr | 183 | "# Common scope notebook を起動する\n" |
| r2 emssr | 184 | "$here = Split-Path -Parent $MyInvocation.MyCommand.Path\n" |
| r2 emssr | 185 | "python \"$here\\server.py\"\n") |
| r2 emssr | 186 | open(os.path.join(dst, "start.sh"), "w", encoding="utf-8", newline="\n").write( |
| r2 emssr | 187 | "#!/bin/sh\n" |
| r2 emssr | 188 | "# Common scope notebook を起動する\n" |
| r2 emssr | 189 | 'exec python3 "$(dirname "$0")/server.py" "$@"\n') |
| r2 emssr | 190 | n = sum(len(f) for _b, _d, f in os.walk(dst)) |
| r2 emssr | 191 | size = sum(os.path.getsize(os.path.join(b, f)) |
| r2 emssr | 192 | for b, _d, fs in os.walk(dst) for f in fs) |
| r2 emssr | 193 | print("%s %d ファイル %.1f MB" % (dst, n, size / 1e6)) |
| r2 emssr | 194 | return 0 |
| r2 emssr | 195 | |
| r2 emssr | 196 | |
| r2 emssr | 197 | def main(argv): |
| r2 emssr | 198 | do_rt = do_ve = True |
| r2 emssr | 199 | version = None |
| r2 emssr | 200 | it = iter(argv) |
| r2 emssr | 201 | for a in it: |
| r2 emssr | 202 | if a == "--runtime": |
| r2 emssr | 203 | do_ve = False |
| r2 emssr | 204 | elif a == "--vendor": |
| r2 emssr | 205 | do_rt = False |
| r2 emssr | 206 | elif a == "--version": |
| r2 emssr | 207 | version = next(it) |
| r2 emssr | 208 | elif a == "--dist": |
| r2 emssr | 209 | return dist(os.path.abspath(next(it))) |
| r2 emssr | 210 | else: |
| r2 emssr | 211 | print(__doc__) |
| r2 emssr | 212 | return 2 |
| r2 emssr | 213 | bad = 0 |
| r2 emssr | 214 | if do_rt: |
| r2 emssr | 215 | print("runtime/ を揃えます") |
| r2 emssr | 216 | bad |= runtime() |
| r2 emssr | 217 | if do_ve: |
| r2 emssr | 218 | try: |
| r2 emssr | 219 | vendor(version) |
| r2 emssr | 220 | web() |
| r2 emssr | 221 | except Exception as e: # noqa: BLE001 |
| r2 emssr | 222 | print("library を取れません: %s" % e) |
| r2 emssr | 223 | print(" network が無い場所では、別の PC で --vendor を実行して vendor/ をコピーします") |
| r2 emssr | 224 | bad = 1 |
| r2 emssr | 225 | if not bad: |
| r2 emssr | 226 | print("") |
| r2 emssr | 227 | print("起動: python server.py") |
| r2 emssr | 228 | return bad |
| r2 emssr | 229 | |
| r2 emssr | 230 | |
| r2 emssr | 231 | if __name__ == "__main__": |
| r2 emssr | 232 | sys.exit(main(sys.argv[1:])) |