feat(installer): 打包时生成并安装词格语言模型 sc.lm
sc.lm 此前只能手工投放,重装即失效,发布包里也没有它。这里把它变成一个被钉住、 可复现、自动参与打包的产品输入。 它不走词库那条路:词库 release 由本仓库之外的构建器产出,资产集合和摘要在产品锁 里逐一钉死,加一项要对方先发新版。改为独立钉住上游语料,与 libime 自己的做法一致。 - language-model/lock.json 钉住上游 URL、语料摘要、转换参数和产物摘要。转换是 确定性的(已在 MSVC 14.44 与 14.50 两套工具链上验证产物摘要一致),所以产物 摘要可以像下载摘要一样钉死。 - scripts/build-language-model.ps1 下载、校验、转换、再校验,写临时文件后改名, 中途失败不会留下摘要对不上的 sc.lm。幂等,摘要对得上直接返回。 - 它只 configure engine/ngram 一个目录,不拉 vcpkg 和 SQLite:转换工具只依赖 kenlm 自己,而打包机上未必装得起引擎的全部依赖。 - Invoke-LocalTest.ps1 打完整包前自动调用它,轻量包本就不带数据文件,不调。 - 打包脚本只断言文件存在,摘要由构建脚本负责,与词库交给 product_lock.py 校验 的分工一致。缺模型让打包失败而不是跳过:引擎对缺模型是静默降级的,运行期不会 暴露,打包是唯一拦得住的地方,package-files.ps1 为此加了反向用例。 模型是 LGPL-2.1-or-later 的第三方数据,声明随之安装为 libime-lm-NOTICE.md。 安装脚本无需改动,它已按通配收集资源目录。产物与语料不进仓库,只进锁与声明。
This commit is contained in:
1 parent
80b1fc42a1
commit
d30d2946e4
10 files changed
+216
No files matched your search
@@ -468,6 +468,16 @@ jobs:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: bash scripts/ci/download-dictionaries.sh
|
||||
|
||||
# The word-lattice language model is the one product input that is neither built from this
|
||||
# repository's sources nor published in the dictionary release: it is converted here from the
|
||||
# ARPA corpus pinned in language-model/lock.json. Both the download and the converted result
|
||||
# are checked against that lock, so this step is reproducible rather than a fetch of whatever
|
||||
# the upstream file server serves today. It configures engine/ngram alone, so it needs no
|
||||
# vcpkg and no SQLite on this runner.
|
||||
- name: Build the word-lattice language model
|
||||
shell: pwsh
|
||||
run: ./scripts/build-language-model.ps1
|
||||
|
||||
- name: Decide the signing mode
|
||||
id: signing
|
||||
shell: pwsh
|
||||
|
||||
@@ -16,4 +16,10 @@ log.txt
|
||||
# this repository, and both the release workflow and a local release rehearsal drop it beside the
|
||||
# components under this name.
|
||||
/MetasequoiaImeDict/
|
||||
|
||||
# 语言模型:来源锁和授权声明进仓库,下载的语料、转换工具的构建目录和编出来的 sc.lm
|
||||
# 不进 —— 它们都能由 scripts/build-language-model.ps1 从 lock.json 重新得到。
|
||||
/language-model/cache/
|
||||
/language-model/sc.lm
|
||||
/language-model/sc.lm.incoming
|
||||
__pycache__/
|
||||
@@ -57,6 +57,13 @@ try {
|
||||
if ($LASTEXITCODE -ne 0) { throw "Settings page build failed ($LASTEXITCODE)" }
|
||||
} finally { Pop-Location }
|
||||
|
||||
# 轻量包不带任何数据文件,所以只有完整包需要语言模型。脚本是幂等的:摘要对得上时
|
||||
# 直接返回,不重下也不重转,因此这里无条件调用不会让日常 test.ps1 变慢。首次运行
|
||||
# 要下 ~75 MB 并转换一次,之后都是秒回。
|
||||
if (-not $Light) {
|
||||
& (Join-Path $repoRoot 'scripts\build-language-model.ps1')
|
||||
}
|
||||
|
||||
& (Join-Path $PSScriptRoot 'Prepare-PackageFiles.ps1') -Light:$Light -IncludeSymbols:$IncludeSymbols `
|
||||
-RepoRoot $repoRoot -TsfDirectory windows -ServerDirectory server -UiHtmlDirectory ui-html -NoticesDirectory .
|
||||
& (Join-Path $PSScriptRoot 'Sign-PackageBinaries-Local.ps1')
|
||||
|
||||
@@ -9,6 +9,9 @@ param(
|
||||
[string]$UiHtmlDirectory = 'ui-html',
|
||||
[string]$HelpCodeDirectory = 'engine/helpcode',
|
||||
[string]$DictionaryDirectory = 'MetasequoiaImeDict',
|
||||
# 词格打分用的语言模型不随词库发布下发,而是由 scripts/build-language-model.ps1
|
||||
# 从钉住的上游语料现场转换到这个目录。
|
||||
[string]$LanguageModelDirectory = 'language-model',
|
||||
# THIRD_PARTY_NOTICES.txt used to sit next to the tip's sources. In the consolidated repository
|
||||
# the notice covers the whole product and lives at the root, one level above windows/, so where
|
||||
# to read it is no longer answered by where the tip is.
|
||||
@@ -66,6 +69,8 @@ $japaneseModel = Join-Path $RepoRoot (Join-Path $DictionaryDirectory 'out\dict_j
|
||||
$japaneseModelLicense = Join-Path $RepoRoot (Join-Path $DictionaryDirectory 'source\mozc_dictionary_oss\README.txt')
|
||||
$englishDb = Join-Path $RepoRoot (Join-Path $DictionaryDirectory 'out\english.db')
|
||||
$othersDb = Join-Path $RepoRoot (Join-Path $DictionaryDirectory 'out\others.db')
|
||||
$languageModel = Join-Path $RepoRoot (Join-Path $LanguageModelDirectory 'sc.lm')
|
||||
$languageModelNotice = Join-Path $RepoRoot (Join-Path $LanguageModelDirectory 'NOTICE.md')
|
||||
|
||||
Assert-PathExists -LiteralPath $RepoRoot -Description '源码仓库根目录'
|
||||
Assert-PathExists -LiteralPath $serverRelease -Description 'Server Release 输出目录'
|
||||
@@ -118,6 +123,11 @@ if 'weight' not in names or pk != ['word', 'display']:
|
||||
throw "英文词库数据库 schema 检查失败:$englishDb"
|
||||
}
|
||||
Assert-PathExists -LiteralPath $othersDb -Description '杂项数据库 others.db'
|
||||
# 摘要校验留在 build-language-model.ps1 里:它既有 lock.json 又刚生成完文件。
|
||||
# 这里只确认文件在,与 msime.db 一样 —— 词库的摘要也是由 product_lock.py 校验的。
|
||||
Assert-PathExists -LiteralPath $languageModel `
|
||||
-Description '词格整句语言模型 sc.lm(用 scripts\build-language-model.ps1 生成)'
|
||||
Assert-PathExists -LiteralPath $languageModelNotice -Description 'libime 语言模型授权声明 NOTICE.md'
|
||||
}
|
||||
|
||||
$targetAppData = Join-Path $PSScriptRoot 'app_data'
|
||||
@@ -142,6 +152,12 @@ else {
|
||||
Copy-Item -LiteralPath $japaneseModelLicense -Destination (Join-Path $targetAppData 'MOZC_DICTIONARY_LICENSE.txt') -Force
|
||||
Copy-Item -LiteralPath $englishDb -Destination (Join-Path $targetAppData 'english.db') -Force
|
||||
Copy-Item -LiteralPath $othersDb -Destination (Join-Path $targetAppData 'others.db') -Force
|
||||
# sc.lm 落在资源目录根下,与 msime.db 同级:engine/contracts/assets 的清单就是这么
|
||||
# 声明这个资源的路径的,引擎按该清单里的文件名去找。
|
||||
Copy-Item -LiteralPath $languageModel -Destination (Join-Path $targetAppData 'sc.lm') -Force
|
||||
# 模型数据是 LGPL-2.1-or-later 的第三方作品,声明必须跟着二进制到用户磁盘上,
|
||||
# 理由同下面 rime-ice 那段。
|
||||
Copy-Item -LiteralPath $languageModelNotice -Destination (Join-Path $targetAppData 'libime-lm-NOTICE.md') -Force
|
||||
|
||||
$defaultConfigPath = Join-Path $targetAppData 'config.default.toml'
|
||||
# 出厂配置来自本仓库的 default_config,不依赖本机是否已安装输入法。
|
||||
|
||||
@@ -29,6 +29,7 @@ pwsh -File ./Prepare-PackageFiles.ps1 -TargetVersion 1.2.3 -RepoRoot .. `
|
||||
- **`THIRD_PARTY_NOTICES.txt` 从 `-NoticesDirectory` 指向的目录取,随安装包装到程序目录。** 合仓后这份声明覆盖整个产品、放在仓根,所以它和 `-TsfDirectory` 分成了两个参数。词库主体含 rime-ice(GPL-3.0)内容,其许可要求保留署名,所以这份文件缺失会让打包直接失败,而不是静默跳过
|
||||
- **`Sign-PackageBinaries-Local.ps1` 和 `Sign-Installer-Local.ps1` 只用于本地验证,CI 不会调用它们。** 它们创建本机自签名证书并尝试写入受信任存储,正式发布走 workflow 里用仓库 secret 中真实证书的签名步骤
|
||||
- 词库不再从相邻的 `MetasequoiaImeDict` 工作目录取,CI 从产品锁指定的 `dict-*` release 下载并校验 SHA256,再放到脚本期望的位置
|
||||
- 语言模型 `sc.lm` 不在那个 release 里。它由 `scripts/build-language-model.ps1` 在打包前从 `language-model/lock.json` 钉住的 libime 语料转换出来,下载摘要和产物摘要都要对上。release workflow 里对应的是「Build the word-lattice language model」这一步,缺了它 `Prepare-PackageFiles.ps1` 会直接失败
|
||||
- `windows-2025` runner 自带 Inno Setup 6.7.1,`Compile-Installer.ps1` 能自己找到 `ISCC.exe`。但它不带 `ChineseSimplified.isl`,`msime_setup.iss` 的 `[Languages]` 段依赖那个文件,所以 workflow 会在编译前按固定 revision 和校验和把它装进去
|
||||
|
||||
## 数据目录
|
||||
@@ -48,6 +49,7 @@ pwsh -File ./Prepare-PackageFiles.ps1 -TargetVersion 1.2.3 -RepoRoot .. `
|
||||
- Windows SDK(提供 `signtool.exe`)
|
||||
- 先初始化本仓 submodule,并完成 `windows/`、`server/` 的 Release 编译以及 `ui-html/` 设置页构建。Release 构建必须生成同目录 PDB;打包脚本会拒绝缺少匹配符号的产物(符号是否随包安装是另一回事,由 `-IncludeSymbols` 决定)。
|
||||
- 在仓库根目录运行 `python scripts/product_lock.py fetch-dictionaries --staging-root .`,下载并验证产品锁中的词库。
|
||||
- 词格整句打分用的 `sc.lm` 由 `scripts\build-language-model.ps1` 从 `language-model\lock.json` 钉住的上游语料转换而来。`Invoke-LocalTest.ps1` 打完整包前会自动调它,一般不用手工执行;首次运行要下 ~75 MB 并转换一次,之后摘要对得上就直接跳过。
|
||||
- `engine/helpcode/helpcodes/` 是本仓的目录,普通检出即有,无需旧 HelpCode 仓库,也无需初始化 submodule。
|
||||
|
||||
## 本地打包路径
|
||||
|
||||
@@ -34,6 +34,8 @@ try {
|
||||
'MetasequoiaImeDict/out/others.db',
|
||||
'MetasequoiaImeDict/out/dict_japanese.dat',
|
||||
'MetasequoiaImeDict/source/mozc_dictionary_oss/README.txt',
|
||||
'language-model/sc.lm',
|
||||
'language-model/NOTICE.md',
|
||||
'ui-html/webview2/shared/runtime.js',
|
||||
'ui-html/webview2/candwnd/index.html',
|
||||
'ui-html/webview2/menu/index.html',
|
||||
@@ -47,6 +49,7 @@ try {
|
||||
if ($LASTEXITCODE -ne 0) { throw 'Failed to create packaging fixture' }
|
||||
& (Join-Path $installer 'Prepare-PackageFiles.ps1') -RepoRoot $fixture -TargetVersion '2026.9.1' -IncludeSymbols
|
||||
foreach ($file in @('app_data/html/webview2/shared/runtime.js', 'app_data/dictionary-manifest.json',
|
||||
'app_data/sc.lm', 'app_data/libime-lm-NOTICE.md',
|
||||
'tsf_dll/32/MetasequoiaImeTsf.dll', 'tsf_dll/32/MetasequoiaImeTsf.pdb',
|
||||
'tsf_dll/64/MetasequoiaImeTsf.dll', 'tsf_dll/64/MetasequoiaImeTsf.pdb',
|
||||
'server_exe/MetasequoiaImeServer.pdb',
|
||||
@@ -87,6 +90,15 @@ try {
|
||||
& (Join-Path $installer 'Prepare-PackageFiles.ps1') -RepoRoot $fixture -TsfDirectory windows -ServerDirectory server -UiHtmlDirectory ui-html -NoticesDirectory . -Light
|
||||
if ([IO.File]::ReadAllText($database) -ne 'preserved user data') { throw 'Light package replaced dictionary data' }
|
||||
if (-not (Test-Path (Join-Path $installer 'app_data/html/webview2/shared/runtime.js'))) { throw 'Light package lost shared contracts' }
|
||||
# 完整包必须带 sc.lm。缺了它引擎是静默降级的:整句候选只是变差,不会报错,所以打包
|
||||
# 这一步是唯一能挡住它的地方。轻量包本来就不带数据文件,不受影响。
|
||||
$languageModelFixture = Join-Path $fixture 'language-model/sc.lm'
|
||||
Remove-Item $languageModelFixture -Force
|
||||
& (Join-Path $installer 'Prepare-PackageFiles.ps1') -RepoRoot $fixture -TsfDirectory windows -ServerDirectory server -UiHtmlDirectory ui-html -NoticesDirectory . -Light
|
||||
$rejected = $false
|
||||
try { & (Join-Path $installer 'Prepare-PackageFiles.ps1') -RepoRoot $fixture } catch { $rejected = $_.Exception.Message -match 'sc\.lm' }
|
||||
if (-not $rejected) { throw 'Missing language model was accepted' }
|
||||
[IO.File]::WriteAllText($languageModelFixture, 'fixture')
|
||||
Remove-Item (Join-Path $fixture 'ui-html/webview2/shared') -Recurse -Force
|
||||
$rejected = $false
|
||||
try { & (Join-Path $installer 'Prepare-PackageFiles.ps1') -RepoRoot $fixture -TsfDirectory windows -ServerDirectory server -UiHtmlDirectory ui-html -NoticesDirectory . } catch { $rejected = $true }
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
# 语言模型 sc.lm 的来源与许可
|
||||
|
||||
本产品安装的 `sc.lm` 是一个中文三元语言模型,用于给输入法的整句候选打分。
|
||||
|
||||
它由 libime 发布的语料模型 `lm_sc.arpa` 转换而来,转换不改变模型内容,只是把
|
||||
ARPA 文本格式压缩成 kenlm 的量化 trie 二进制格式。
|
||||
|
||||
- 上游项目:libime — https://github.com/fcitx/libime
|
||||
- 数据来源:https://download.fcitx-im.org/data/lm_sc.arpa-20260629.tar.zst
|
||||
- 版权:Copyright (C) 2022~2026 CSSlayer <wengxt@gmail.com>
|
||||
- 许可:GNU Lesser General Public License, version 2.1 or later (LGPL-2.1-or-later)
|
||||
|
||||
LGPL-2.1 的完整正文随本产品一同安装,见 `THIRD_PARTY_NOTICES.txt` 中 kenlm 一节
|
||||
所附的许可证全文;该正文同样适用于本模型数据。
|
||||
|
||||
读取该模型的代码是 kenlm(https://github.com/kpu/kenlm),其来源、裁剪范围与许可
|
||||
另见 `THIRD_PARTY_NOTICES.txt`。仓库根 `LICENSE` 不覆盖也不重新授权上述第三方数据。
|
||||
@@ -0,0 +1,26 @@
|
||||
# 词格整句打分用的语言模型
|
||||
|
||||
本目录钉住 `sc.lm` 的来源,并放置生成它所需的声明。`sc.lm` 是一个 kenlm 三元模型,
|
||||
`engine/ngram` 把它 mmap 起来给词格的整句候选打分;缺了它引擎会退回启发式打分,
|
||||
整句候选只是变差,不会停止工作(见 `engine/docs/runtime-architecture.md`)。
|
||||
|
||||
模型本身不在仓库里,也不随词库发布下发。它由 `scripts/build-language-model.ps1`
|
||||
从 `lock.json` 钉住的上游 ARPA 现场转换而来:
|
||||
|
||||
```powershell
|
||||
.\scripts\build-language-model.ps1
|
||||
```
|
||||
|
||||
该脚本下载 ARPA 压缩包、校验其 SHA-256、按 `lock.json` 里的参数转换成 `sc.lm`,
|
||||
再校验产物的 SHA-256。转换是确定性的,所以产物摘要可以像下载摘要一样钉死。
|
||||
脚本是幂等的:`sc.lm` 已存在且摘要正确时直接返回,不重新下载也不重新转换。
|
||||
`installer\Invoke-LocalTest.ps1` 在打完整包前会自动调用它,因此日常跑 `test.ps1`
|
||||
不需要手工执行。
|
||||
|
||||
上游与 libime 用的是同一份数据和同一组参数(见 libime 的 `data/CMakeLists.txt`),
|
||||
这样我们编出来的模型与 fcitx5 发行版里的 `zh_CN.lm` 等价。`lock.json` 的
|
||||
`build.order` 必须与 `engine/ngram/CMakeLists.txt` 里的 `KENLM_MAX_ORDER` 一致,
|
||||
否则运行时读不懂编出来的文件。
|
||||
|
||||
`NOTICE.md` 是随产品安装的授权声明,安装后名为 `libime-lm-NOTICE.md`。它进仓库,
|
||||
因为它是需要评审的声明文本,不是可以重新生成的产物。
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"schema_version": 1,
|
||||
"source": {
|
||||
"url": "https://download.fcitx-im.org/data/lm_sc.arpa-20260629.tar.zst",
|
||||
"archive_sha256": "06808333b9173e5374cf2cb5afc12d08f5625bf9abb536489cac376fc05f2e7f",
|
||||
"member": "lm_sc.arpa"
|
||||
},
|
||||
"build": {
|
||||
"order": 3,
|
||||
"flags": ["-s", "-a", "22", "-q", "4", "trie"]
|
||||
},
|
||||
"output": {
|
||||
"path": "sc.lm",
|
||||
"sha256": "3588b3942c8fd62e1a6bd3bae8c7cadc0faf928c175b245224ed475862218387",
|
||||
"bytes": 34736327
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
# 生成词格整句打分用的 sc.lm。
|
||||
#
|
||||
# 词库是从钉住的发布下载的,语言模型不是:它的上游是 libime 的 ARPA 语料,我们按
|
||||
# libime 自己那组参数现场转换。转换是确定性的,所以 language-model\lock.json 既钉
|
||||
# 下载的摘要,也钉产物的摘要 —— 两头都校验过,本地和 CI 才会得到同一个文件。
|
||||
#
|
||||
# 幂等:sc.lm 已在且摘要正确时直接返回。因此 Invoke-LocalTest.ps1 可以在每次打完整
|
||||
# 包前无条件调用它,日常 test.ps1 不会为此多花时间。
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string]$RepoRoot = (Split-Path -Parent $PSScriptRoot),
|
||||
# 下载的压缩包与解出来的 ARPA 都留在这里。ARPA 有 ~190 MB,保留它是为了改转换参数
|
||||
# 时不必重下;删掉也只是下次多下一次。
|
||||
[string]$CacheDirectory,
|
||||
# 即使摘要已经对上也重新转换一次。改了 lock.json 的 build 参数时用。
|
||||
[switch]$Force
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
Set-StrictMode -Version Latest
|
||||
|
||||
$modelDirectory = Join-Path $RepoRoot 'language-model'
|
||||
$lockPath = Join-Path $modelDirectory 'lock.json'
|
||||
if (-not (Test-Path -LiteralPath $lockPath)) {
|
||||
throw "语言模型的来源锁不存在:$lockPath"
|
||||
}
|
||||
$lock = Get-Content -LiteralPath $lockPath -Raw | ConvertFrom-Json
|
||||
if ($lock.schema_version -ne 1) {
|
||||
throw "不支持的 lock.json schema_version:$($lock.schema_version)"
|
||||
}
|
||||
if (-not $CacheDirectory) { $CacheDirectory = Join-Path $modelDirectory 'cache' }
|
||||
|
||||
$modelPath = Join-Path $modelDirectory $lock.output.path
|
||||
|
||||
function Test-Digest {
|
||||
param([Parameter(Mandatory)][string]$LiteralPath, [Parameter(Mandatory)][string]$Expected)
|
||||
if (-not (Test-Path -LiteralPath $LiteralPath)) { return $false }
|
||||
return (Get-FileHash -Algorithm SHA256 -LiteralPath $LiteralPath).Hash.ToLower() -eq $Expected.ToLower()
|
||||
}
|
||||
|
||||
function Assert-Digest {
|
||||
param([Parameter(Mandatory)][string]$LiteralPath, [Parameter(Mandatory)][string]$Expected,
|
||||
[Parameter(Mandatory)][string]$Description)
|
||||
$actual = (Get-FileHash -Algorithm SHA256 -LiteralPath $LiteralPath).Hash.ToLower()
|
||||
if ($actual -ne $Expected.ToLower()) {
|
||||
throw "$Description 的 SHA256 与 lock.json 不符:期望 $($Expected.ToLower()),实得 $actual($LiteralPath)"
|
||||
}
|
||||
}
|
||||
|
||||
if (-not $Force -and (Test-Digest -LiteralPath $modelPath -Expected $lock.output.sha256)) {
|
||||
Write-Host "语言模型已就绪:$modelPath"
|
||||
return
|
||||
}
|
||||
|
||||
New-Item -ItemType Directory -Path $CacheDirectory -Force | Out-Null
|
||||
$archiveName = Split-Path -Leaf ([uri]$lock.source.url).AbsolutePath
|
||||
$archivePath = Join-Path $CacheDirectory $archiveName
|
||||
$arpaPath = Join-Path $CacheDirectory $lock.source.member
|
||||
|
||||
if (-not (Test-Digest -LiteralPath $archivePath -Expected $lock.source.archive_sha256)) {
|
||||
Write-Host "下载语料模型:$($lock.source.url)"
|
||||
# 摘要没对上的残留必须先删:curl 的断点续传会把两次不同的下载接在一起。
|
||||
if (Test-Path -LiteralPath $archivePath) { Remove-Item -LiteralPath $archivePath -Force }
|
||||
curl.exe -L --fail --retry 3 -o $archivePath $lock.source.url
|
||||
if ($LASTEXITCODE -ne 0) { throw "下载语料模型失败($LASTEXITCODE):$($lock.source.url)" }
|
||||
}
|
||||
Assert-Digest -LiteralPath $archivePath -Expected $lock.source.archive_sha256 -Description '语料模型压缩包'
|
||||
|
||||
# Windows 自带的 tar 是 bsdtar,直接认 zstd,不需要另装 zstd。
|
||||
if (Test-Path -LiteralPath $arpaPath) { Remove-Item -LiteralPath $arpaPath -Force }
|
||||
tar.exe -xf $archivePath -C $CacheDirectory
|
||||
if ($LASTEXITCODE -ne 0) { throw "解压语料模型失败($LASTEXITCODE):$archivePath" }
|
||||
if (-not (Test-Path -LiteralPath $arpaPath)) {
|
||||
throw "压缩包里没有 $($lock.source.member):$archivePath"
|
||||
}
|
||||
|
||||
# 只 configure engine\ngram 这一个目录,不走 engine\CMakeLists.txt。转换工具只依赖
|
||||
# kenlm 自己(零 Boost、零 SQLite),为它拉起整个引擎的依赖没有意义,在只下载了
|
||||
# 二进制产物的打包机上还未必装得起来。
|
||||
$toolBuild = Join-Path $CacheDirectory 'build-tool'
|
||||
$toolPath = Join-Path $toolBuild 'Release\metasequoia_ime_build_binary.exe'
|
||||
if (-not (Test-Path -LiteralPath $toolPath)) {
|
||||
cmake -S (Join-Path $RepoRoot 'engine\ngram') -B $toolBuild -DMETASEQUOIA_IME_BUILD_NGRAM_TOOLS=ON
|
||||
if ($LASTEXITCODE -ne 0) { throw "配置转换工具失败($LASTEXITCODE)" }
|
||||
cmake --build $toolBuild --config Release --target metasequoia_ime_build_binary --parallel
|
||||
if ($LASTEXITCODE -ne 0) { throw "构建转换工具失败($LASTEXITCODE)" }
|
||||
}
|
||||
if (-not (Test-Path -LiteralPath $toolPath)) {
|
||||
# 单配置生成器(Ninja、Makefiles)不套 Release 子目录。
|
||||
$toolPath = Join-Path $toolBuild 'metasequoia_ime_build_binary.exe'
|
||||
if (-not (Test-Path -LiteralPath $toolPath)) { throw "没有找到转换工具:$toolBuild" }
|
||||
}
|
||||
|
||||
# 先写到临时文件再改名:转换中途失败不应留下一个摘要对不上的 sc.lm,让下次运行
|
||||
# 以为只是缓存脏了。
|
||||
$stagingPath = "$modelPath.incoming"
|
||||
if (Test-Path -LiteralPath $stagingPath) { Remove-Item -LiteralPath $stagingPath -Force }
|
||||
& $toolPath @($lock.build.flags) $arpaPath $stagingPath
|
||||
if ($LASTEXITCODE -ne 0) { throw "转换语言模型失败($LASTEXITCODE)" }
|
||||
Assert-Digest -LiteralPath $stagingPath -Expected $lock.output.sha256 -Description '语言模型 sc.lm'
|
||||
Move-Item -LiteralPath $stagingPath -Destination $modelPath -Force
|
||||
|
||||
Write-Host "语言模型生成完成:$modelPath($((Get-Item -LiteralPath $modelPath).Length) 字节)"
|
||||
Reference in new issue
Block a user