param( [Parameter(Mandatory = $true)][string]$ProjectRoot, [switch]$QuickAppOnly, [switch]$Clean ) Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' $ProjectRoot = (Resolve-Path -LiteralPath $ProjectRoot).Path $toolBin = Join-Path $PSScriptRoot 'temp\glyphix-v0.7.2-windows-x64\bin' $gx = Join-Path $toolBin 'gx.exe' if (-not (Test-Path -LiteralPath $gx)) { throw "Glyphix toolchain not found: $gx" } $manifestPath = Join-Path $ProjectRoot 'src\manifest.json' $manifest = Get-Content -LiteralPath $manifestPath -Raw | ConvertFrom-Json if ($manifest.package -cnotmatch '^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)+$') { throw 'Invalid Glyphix package name' } $quickAppTargets = @('generic-watch-466x466-quickapp', 'generic-watch-390x450-quickapp') $targets = if ($QuickAppOnly) { $quickAppTargets } else { @('generic-watch-466x466', 'generic-watch-390x450', 'generic-watch-466x466-quickapp', 'generic-watch-390x450-quickapp') } $originalPath = $env:Path $buildRoot = $ProjectRoot $mappedDrive = $null # Bundled pngquant still uses MAX_PATH; keep deeply generated apps buildable. if ($IsWindows -and $ProjectRoot.Length + $manifest.package.Length + 150 -gt 245) { $subst = Join-Path $env:SystemRoot 'System32\subst.exe' foreach ($letter in @('Z','Y','X','W','V','U','T','S','R')) { if (Test-Path -LiteralPath ($letter + ':\')) { continue } & $subst ($letter + ':') $ProjectRoot 2>&1 | Out-Null if ($LASTEXITCODE -eq 0) { $mappedDrive = $letter + ':' $buildRoot = $mappedDrive + '\' break } } if (-not $mappedDrive) { throw 'No temporary drive available for the vendor MAX_PATH workaround; use a shorter ProjectRoot' } } Push-Location $buildRoot try { $env:Path = "$toolBin;$originalPath" $availableDevices = @(& $gx list device) if ($LASTEXITCODE -ne 0) { throw 'Unable to read Glyphix device profiles' } foreach ($device in $targets) { if ($availableDevices -cnotcontains $device) { throw "Missing Glyphix device profile: $device" } } if ($Clean) { & $gx clean if ($LASTEXITCODE -ne 0) { throw 'Glyphix clean failed' } } foreach ($device in $targets) { $arguments = @('build', '-d', $device) if (-not $device.Contains('quickapp')) { $arguments += '-e' } $output = @(& $gx @arguments 2>&1 | ForEach-Object { $_.ToString() }) $output | ForEach-Object { Write-Host $_ } if ($LASTEXITCODE -ne 0 -or ($output -join "`n") -match '(?im)^\s*(?:error|fatal):\s') { throw "Glyphix build failed for $device" } } foreach ($device in $quickAppTargets) { $deliveryRoot = Join-Path $ProjectRoot ".glyphix-work\dist\$device\$($manifest.package)" foreach ($fileName in @('bundle.pkg', 'manifest.json', 'icon.png')) { $artifact = Join-Path $deliveryRoot $fileName if (-not (Test-Path -LiteralPath $artifact -PathType Leaf) -or (Get-Item -LiteralPath $artifact).Length -eq 0) { throw "Missing or empty QuickApp artifact: $artifact" } } $packedManifest = Get-Content -LiteralPath (Join-Path $deliveryRoot 'manifest.json') -Raw | ConvertFrom-Json if ($packedManifest.package -cne $manifest.package -or $packedManifest.versionName -cne $manifest.versionName -or $packedManifest.versionCode -ne $manifest.versionCode) { throw "QuickApp package/version mismatch: $device" } $icon = [IO.File]::ReadAllBytes((Join-Path $deliveryRoot 'icon.png')) if ($icon.Length -lt 8 -or [Convert]::ToHexString($icon[0..7]) -cne '89504E470D0A1A0A') { throw "Invalid QuickApp PNG icon: $device" } foreach ($fileName in @('bundle.pkg', 'manifest.json', 'icon.png')) { $artifact = Join-Path $deliveryRoot $fileName [pscustomobject]@{ Package = $manifest.package Device = $device File = $fileName Bytes = (Get-Item -LiteralPath $artifact).Length SHA256 = (Get-FileHash -LiteralPath $artifact -Algorithm SHA256).Hash } } } } finally { Pop-Location if ($mappedDrive) { & $subst $mappedDrive /D | Out-Null } $env:Path = $originalPath }