Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 68 additions & 55 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,31 +41,27 @@ jobs:

# 配布先に VC++ 再頒布可能パッケージのインストールを要求しないよう CRT を静的リンクする。
# hidapi は cc 経由で C を混ぜるが、cc は crt-static を見て /MT に切り替える。
- name: Build hid_probe
- name: Build binaries
if: steps.detect.outputs.exists == 'true'
env:
RUSTFLAGS: -C target-feature=+crt-static
run: cargo build --bin hid_probe --release
run: cargo build --bin frametap --bin hid_probe --release

# import を読む道具に llvm-readobj を選ぶ。dumpbin は Visual Studio の版とエディションで
# 置き場所が変わるが、llvm-readobj は rustup が toolchain 配下の固定位置に置く。
- name: Package hid_probe
#
# system かどうかを DLL 名の一覧では決めない。eframe は opengl32.dll や dwmapi.dll など
# 一覧に載せ忘れやすいものを引き、載せ忘れは「runner の System32 から複製して配る」という
# 形で出る。解決した先が System32 の下かどうかで判定すれば、一覧を保守しなくても漏れない。
- name: Package binaries
if: steps.detect.outputs.exists == 'true'
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'

$exePath = 'target/release/hid_probe.exe'
$stageDir = 'dist'

# Windows 10 / 11 に標準で入る DLL。api-ms-win-* (UCRT を含む) は OS の一部。
# ここに無い import は配布先に無い可能性があるため exe に同梱する。
$systemDllNames = @(
'advapi32.dll', 'bcrypt.dll', 'cfgmgr32.dll', 'crypt32.dll', 'hid.dll',
'kernel32.dll', 'kernelbase.dll', 'msvcrt.dll', 'ntdll.dll', 'ole32.dll',
'oleaut32.dll', 'powrprof.dll', 'secur32.dll', 'setupapi.dll', 'shell32.dll',
'user32.dll', 'userenv.dll', 'winmm.dll', 'ws2_32.dll'
)
$exeNames = @('frametap.exe', 'hid_probe.exe')
$systemDir = Join-Path $env:SystemRoot 'System32'

$sysroot = (rustc --print sysroot).Trim()
$readobj = Get-ChildItem -Path (Join-Path $sysroot 'lib/rustlib/*/bin/llvm-readobj.exe') |
Expand All @@ -74,69 +70,86 @@ jobs:
throw "llvm-readobj が $sysroot 配下に無い"
}

# --coff-imports は Import と DelayImport のブロックを出し、各ブロックの Name 行が DLL 名になる。
$readobjOutput = & $readobj.FullName --coff-imports $exePath
if ($LASTEXITCODE -ne 0) {
throw "llvm-readobj が失敗した (exit $LASTEXITCODE)"
}

$importedDllNames = @($readobjOutput |
Select-String -Pattern '^\s+Name:\s+(\S+)$' |
ForEach-Object { $_.Matches[0].Groups[1].Value } |
Sort-Object -Unique)

# Windows の exe は必ず kernel32 等を import する。0 件なら読み取りが壊れている。
if ($importedDllNames.Count -eq 0) {
Write-Host $readobjOutput
throw "import が 1 件も取れなかった"
}

New-Item -ItemType Directory -Force -Path $stageDir | Out-Null
Copy-Item -Path $exePath -Destination $stageDir -Force

# 配布先に無い可能性のある DLL は、PATH と VC 再頒布ディレクトリから探して artifact に入れる。
# 探す先は PATH と VC 再頒布ディレクトリ。PATH には System32 が入るので、
# OS の一部である DLL もここで解決される。System32 の下かどうかは後で見る。
$vcRedistGlob = 'C:\Program Files*\Microsoft Visual Studio\*\*\VC\Redist\MSVC\*\x64\Microsoft.VC*.CRT'
$vcRedistDirs = Get-ChildItem -Path $vcRedistGlob -Directory -ErrorAction SilentlyContinue
$searchDirs = @($env:PATH -split ';') + @($vcRedistDirs | ForEach-Object { $_.FullName })

Write-Host 'hid_probe.exe が import する DLL:'
New-Item -ItemType Directory -Force -Path $stageDir | Out-Null

$unresolved = @()
foreach ($name in $importedDllNames) {
$lowered = $name.ToLowerInvariant()
if ($systemDllNames -contains $lowered -or $lowered -like 'api-ms-win-*') {
Write-Host " $name (system)"
continue
foreach ($exeName in $exeNames) {
$exePath = Join-Path 'target/release' $exeName
if (-not (Test-Path -Path $exePath -PathType Leaf)) {
throw "$exePath が無い"
}

# --coff-imports は Import と DelayImport のブロックを出し、各ブロックの Name 行が DLL 名になる。
$readobjOutput = & $readobj.FullName --coff-imports $exePath
if ($LASTEXITCODE -ne 0) {
throw "llvm-readobj が $exeName で失敗した (exit $LASTEXITCODE)"
}

$resolved = $searchDirs |
Where-Object { $_ } |
ForEach-Object { Join-Path $_ $name } |
Where-Object { Test-Path -Path $_ -PathType Leaf } |
Select-Object -First 1

if ($resolved) {
Copy-Item -Path $resolved -Destination $stageDir -Force
Write-Host " $name (同梱: $resolved)"
} else {
Write-Host " $name (見つからないため同梱できない)"
$unresolved += $name
$importedDllNames = @($readobjOutput |
Select-String -Pattern '^\s+Name:\s+(\S+)$' |
ForEach-Object { $_.Matches[0].Groups[1].Value } |
Sort-Object -Unique)

# Windows の exe は必ず kernel32 等を import する。0 件なら読み取りが壊れている。
if ($importedDllNames.Count -eq 0) {
Write-Host $readobjOutput
throw "$exeName の import が 1 件も取れなかった"
}

Copy-Item -Path $exePath -Destination $stageDir -Force

Write-Host "$exeName が import する DLL:"
foreach ($name in $importedDllNames) {
# api-ms-win-* は OS の一部だが、実体のあるファイルとして解決されないことがある。
if ($name -like 'api-ms-win-*') {
Write-Host " $name (system)"
continue
}

$resolved = $searchDirs |
Where-Object { $_ } |
ForEach-Object { Join-Path $_ $name } |
Where-Object { Test-Path -Path $_ -PathType Leaf } |
Select-Object -First 1

if (-not $resolved) {
Write-Host " $name (見つからないため同梱できない)"
$unresolved += "$exeName の $name"
continue
}

$full = (Resolve-Path -LiteralPath $resolved).Path
if ($full.StartsWith($systemDir, [System.StringComparison]::OrdinalIgnoreCase)) {
Write-Host " $name (system: $full)"
continue
}

Copy-Item -LiteralPath $full -Destination $stageDir -Force
Write-Host " $name (同梱: $full)"
}

Write-Host ''
}

if ($unresolved.Count -gt 0) {
throw "配布先に無い可能性のある DLL を同梱できなかった: $($unresolved -join ', ')"
}

Write-Host ''
Write-Host 'artifact の中身:'
Get-ChildItem -Path $stageDir | ForEach-Object {
Write-Host " $($_.Name) ($($_.Length) bytes)"
}

- name: Upload hid_probe
- name: Upload binaries
if: steps.detect.outputs.exists == 'true'
uses: actions/upload-artifact@v4
with:
name: hid_probe-windows-x64
name: frametap-windows-x64
path: dist/
if-no-files-found: error
Loading