- vendor/so-novel: 上游 v1.12.0 (规则驱动的书源抓取/下载引擎) - tools/src: ToolMain 封装 search/download/toc/sources,stdout 只输出 JSON - tools/build.ps1: 自动定位/下载 JDK21,按平台显式激活 javet 原生库 profile - bin/novel.ps1|.cmd: 命令行入口(人读输出或 -Json) - SKILL.md/README.md: 使用说明与下载原理分析
115 lines
5.5 KiB
PowerShell
115 lines
5.5 KiB
PowerShell
# Builds the so-novel engine jar (from the submodule) and the tool wrapper classes.
|
||
# Run once after clone / submodule update: pwsh tools\build.ps1
|
||
param(
|
||
[switch]$Force, # rebuild so-novel even if runtime\app.jar exists
|
||
[switch]$RefreshRules # re-copy bundle\rules over runtime\rules
|
||
)
|
||
|
||
$ErrorActionPreference = 'Stop'
|
||
. (Join-Path $PSScriptRoot 'lib.ps1')
|
||
|
||
$p = Get-ToolPaths
|
||
Write-Host "[build] project root : $($p.Root)"
|
||
|
||
# ---------------------------------------------------------------- 1. JDK 21
|
||
$jdk = Find-Jdk21 -AllowDownload
|
||
if (-not $jdk) { throw 'No JDK 21 available and download failed. Set EBOOK_NOVEL_JAVA_HOME.' }
|
||
Write-Host "[build] JDK 21 : $jdk"
|
||
$javac = Join-Path $jdk 'bin\javac.exe'
|
||
if (-not (Test-Path $javac)) { throw "javac not found in $jdk (a JRE is not enough)" }
|
||
|
||
# ---------------------------------------------------------------- 2. so-novel app.jar
|
||
New-Item -ItemType Directory -Force $p.Runtime | Out-Null
|
||
if ($Force -or -not (Test-Path $p.AppJar)) {
|
||
if (-not (Test-Path (Join-Path $p.Vendor 'pom.xml'))) {
|
||
throw 'vendor/so-novel is empty. Run: git submodule update --init --recursive'
|
||
}
|
||
$mvn = Get-Command mvn -ErrorAction SilentlyContinue
|
||
if (-not $mvn) { throw 'Maven (mvn) is required to build so-novel. Install Maven or use a prebuilt app.jar.' }
|
||
|
||
Write-Host '[build] mvn package so-novel ...' -ForegroundColor Cyan
|
||
# so-novel 用 <os><arch>x86_64</arch> 激活 javet 原生库 profile,而 Windows JDK 报的是 amd64,
|
||
# 自动激活会失效(导致 JS 规则的书源全部 "Javet library ... not found"),这里显式指定。
|
||
$platformProfile = switch -Regex ("$([System.Runtime.InteropServices.RuntimeInformation]::OSDescription)|$([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture)") {
|
||
'Windows.*X64' { 'windows-x64' }
|
||
'Linux.*X64' { 'linux-x64' }
|
||
'Linux.*Arm64' { 'linux-arm64' }
|
||
'OSX|Darwin.*X64' { 'macos-x64' }
|
||
'OSX|Darwin.*Arm64' { 'macos-arm64' }
|
||
default { $null }
|
||
}
|
||
if ($platformProfile) {
|
||
Write-Host "[build] javet profile: $platformProfile" -ForegroundColor Cyan
|
||
} else {
|
||
Write-Warning 'Unknown platform, javet native library may be missing (JS-based rules will fail).'
|
||
}
|
||
$previousJavaHome = $env:JAVA_HOME
|
||
$previousPath = $env:PATH
|
||
try {
|
||
$env:JAVA_HOME = $jdk
|
||
$env:PATH = (Join-Path $jdk 'bin') + ';' + $previousPath
|
||
Push-Location $p.Vendor
|
||
try {
|
||
$mvnArgs = @('-B', '-DskipTests', '-Dlaunch4j.skip=true')
|
||
if ($platformProfile) { $mvnArgs += @('-P', $platformProfile) }
|
||
$mvnArgs += @('clean', 'package')
|
||
& $mvn.Source @mvnArgs
|
||
if ($LASTEXITCODE -ne 0) { throw "mvn package failed with exit code $LASTEXITCODE" }
|
||
} finally {
|
||
Pop-Location
|
||
}
|
||
} finally {
|
||
$env:JAVA_HOME = $previousJavaHome
|
||
$env:PATH = $previousPath
|
||
}
|
||
|
||
$built = Join-Path $p.Vendor 'target\app-jar-with-dependencies.jar'
|
||
if (-not (Test-Path $built)) { $built = Join-Path $p.Vendor 'target\app.jar' }
|
||
if (-not (Test-Path $built)) { throw 'so-novel build produced no jar in target/' }
|
||
Copy-Item $built $p.AppJar -Force
|
||
Write-Host "[build] app.jar : $($p.AppJar) ($([math]::Round((Get-Item $p.AppJar).Length / 1MB, 1)) MB)"
|
||
} else {
|
||
Write-Host "[build] app.jar : up to date ($([math]::Round((Get-Item $p.AppJar).Length / 1MB, 1)) MB)"
|
||
}
|
||
|
||
# ---------------------------------------------------------------- 3. rule bundles
|
||
New-Item -ItemType Directory -Force $p.Rules | Out-Null
|
||
$sourceRules = Join-Path $p.Vendor 'bundle\rules'
|
||
foreach ($file in (Get-ChildItem $sourceRules -Filter '*.json*' -File)) {
|
||
$target = Join-Path $p.Rules $file.Name
|
||
if ($RefreshRules -or -not (Test-Path $target)) {
|
||
Copy-Item $file.FullName $target -Force
|
||
}
|
||
}
|
||
Write-Host "[build] rules : $((Get-ChildItem $p.Rules -File).Count) files in $($p.Rules)"
|
||
|
||
# ---------------------------------------------------------------- 4. tool classes
|
||
if (Test-Path $p.Classes) { Remove-Item $p.Classes -Recurse -Force }
|
||
New-Item -ItemType Directory -Force $p.Classes | Out-Null
|
||
$sources = @(Get-ChildItem (Join-Path $p.Tools 'src') -Filter '*.java' -Recurse | ForEach-Object { $_.FullName })
|
||
Write-Host '[build] javac : tool wrapper ...' -ForegroundColor Cyan
|
||
& $javac '-encoding' 'UTF-8' '--release' '21' '-cp' $p.AppJar '-d' $p.Classes @sources
|
||
if ($LASTEXITCODE -ne 0) { throw "javac failed with exit code $LASTEXITCODE" }
|
||
|
||
# Optional single-file tool jar (a plain classes dir works too; JBR has no jar.exe).
|
||
$jarTool = Find-JarTool
|
||
if ($jarTool) {
|
||
& $jarTool '--create' '--file' $p.ToolJar '-C' $p.Classes '.'
|
||
if ($LASTEXITCODE -ne 0) { Write-Warning 'jar packaging failed; the classes directory will be used instead.' }
|
||
}
|
||
|
||
# ---------------------------------------------------------------- 5. runtime config
|
||
New-Item -ItemType Directory -Force $p.Books | Out-Null
|
||
if ($Force -or -not (Test-Path $p.Config)) {
|
||
$template = Get-Content (Join-Path $p.Tools 'config.ini.template') -Raw
|
||
$ini = $template.Replace('@BOOKS@', ($p.Books -replace '\\', '/'))
|
||
$ini = $ini.Replace('@RULES@', ((Join-Path $p.Rules 'main.json') -replace '\\', '/'))
|
||
[System.IO.File]::WriteAllText($p.Config, $ini, (New-Object System.Text.UTF8Encoding($false)))
|
||
Write-Host "[build] config.ini : $($p.Config)"
|
||
} else {
|
||
Write-Host "[build] config.ini : kept existing ($($p.Config))"
|
||
}
|
||
|
||
Write-Host ''
|
||
Write-Host 'Build finished. Try: pwsh bin\novel.ps1 sources' -ForegroundColor Green
|