Files
msime-windows/scripts/build-dictionary.py
Lu Fan(SonnyCalcr) 9b9326f153 feat(scripts): 出厂词库不再带内置快捷短语 (#1057)
本地构建不再读取 msime 的 mix/quick_phrases.txt,quick_parases 只建空表,
K 模式、设置页新增的用户快捷短语和安装回放仍写在这张表里。check_outputs
要求该表存在且为空;--compare-release 不再比对这张表。
2026-10-18 13:33:39 +08:00

109 lines
5.9 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""从 msime-dictionary 的文本源数据本地构建 Windows 端词库,写到 MetasequoiaImeDict/out/。
读取 msime-dictionary checkout 的 sources/ 与 custom/,加上 scripts/dictionary_build/inputs.lock.json 固定的
仓外文件(msime 的读音纠错、emoji/颜文字表,ECDICT,SCOWL,rime-jp_sela),产出:
msime.db 全拼表(含地名补充、自定义词条、读音纠错)、86 五笔、空的快捷短语表(出厂不带内置短语)、japanese_lexicon
english.db 英文候选、中英双向释义(ECDICT)、sources/cn-en/ 的中译英释义(全部义项)、custom/translations.txt 覆盖
others.db emoji、颜文字、符号(sources/symbols/ 的万象符号表,剔除 wanxiang-unrenderable.txt 列出的)
dict_japanese.dat 日文整句模型(sources/japanese/ 的 Mozc 词库)
以及 Mozc 与 SCOWL 的声明、dictionary-manifest.json、SHA256SUMS.txt、local-dictionary.json
构建规则移植自 msime 的 Rust 构建器(crates/dict-builder)。--compare-release 把结果与官方 Release 逐表对比。
替换 out/ 前,如果里面是产品锁钉住的旧词库,先整份备份到 MetasequoiaImeDict/out.bak/。
用法:
python scripts/build-dictionary.py # 默认读与本仓同级的 msime-dictionary
python scripts/build-dictionary.py --dictionary D:/msime-dictionary
python scripts/build-dictionary.py --compare-release dict-v2.0.13 # 构建后与官方 Release 逐表对比
python scripts/build-dictionary.py --verify # 只校验 out/ 是否仍是本脚本构建的结果
python scripts/build-dictionary.py --if-stale # 源数据或构建脚本没变就跳过(installer\test.ps1 用)
构建 others.db 需要 pypinyin(python -m pip install pypinyin)。
"""
from __future__ import annotations
import argparse
import os
import sqlite3
import subprocess
import sys
import tempfile
import time
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent))
from dictionary_build import build, compare, inputs # noqa: E402
def default_dictionary() -> Path:
if os.environ.get("MSIME_DICTIONARY"):
return Path(os.environ["MSIME_DICTIONARY"])
return build.ROOT.parent / "msime-dictionary"
def main() -> None:
parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument("--dictionary", type=Path, default=None,
help="msime-dictionary checkout,默认取环境变量 MSIME_DICTIONARY,否则是本仓同级的 msime-dictionary")
parser.add_argument("--msime-resources", type=Path, default=None,
help="改用这个目录下的 msime resources/dictionary-sources 文件(本地修改用),不按锁校验")
parser.add_argument("--compare-release", metavar="TAG", help="构建后与这个 dict-v* Release 逐表对比,不一致时失败")
parser.add_argument("--verify", action="store_true", help="只校验 out/ 是否仍是本脚本构建的结果")
parser.add_argument("--if-stale", action="store_true",
help="out/ 已由当前 msime-dictionary 提交和当前构建脚本构建、且工作区干净时跳过,否则重新构建")
args = parser.parse_args()
if args.verify:
marker = build.verify_out()
state = ",有未提交改动" if marker.get("dirty") else ""
commit = marker["custom_dictionary_commit"][:12] or "不是 Git 工作区"
print(f"词库已就绪:{build.OUT}(msime-dictionary {commit}{state})")
return
dictionary = (args.dictionary or default_dictionary()).resolve()
if not (dictionary / "sources").is_dir() or not (dictionary / "custom").is_dir():
raise SystemExit(f"{dictionary} 不是 msime-dictionary checkout(没有 sources/ 和 custom/),用 --dictionary 指定")
commit, dirty = inputs.git_state(dictionary)
print(f"msime-dictionary:{dictionary}({commit[:12] or '不是 Git 工作区'}{',有未提交改动' if dirty else ''})")
if args.if_stale and not args.compare_release:
reason = build.stale_reason(commit, dirty or args.msime_resources is not None)
if reason is None:
print(f"词库已是最新:{build.OUT}")
return
print(f"重新构建词库:{reason}")
mismatched = inputs.check_upstream(dictionary)
if mismatched:
print(f"提示:{len(mismatched)} 个文件与 upstream.lock.json 记录的不同(本地改过的上游文件或生成表):"
f"{', '.join(mismatched[:5])}{' ...' if len(mismatched) > 5 else ''}")
source = inputs.fetch(dictionary, build.CACHE, args.msime_resources)
started = time.perf_counter()
build.DICT_ROOT.mkdir(parents=True, exist_ok=True)
# 整套构建并检查通过之前不动已有的 out/。
with tempfile.TemporaryDirectory(dir=build.DICT_ROOT, prefix="incoming-") as temporary:
staging = Path(temporary)
build.build_all(source, staging)
build.check_outputs(staging)
build.write_manifests(staging, commit, dirty or args.msime_resources is not None, source.msime_commit)
if args.compare_release:
problems = compare.compare(staging, args.compare_release, build.DICT_ROOT / "cache", commit,
source.msime_commit)
for problem in problems:
print(f" 不一致:{problem}")
if problems:
raise SystemExit(f"与 {args.compare_release} 不一致,out/ 未替换")
build.install(staging)
build.verify_out()
print(f"词库构建完毕({time.perf_counter() - started:.0f}s):{build.OUT}")
if __name__ == "__main__":
try:
main()
except (OSError, ValueError, KeyError, sqlite3.Error, subprocess.CalledProcessError) as error:
raise SystemExit(f"失败:{error}") from error