ci(nuget): split symbols and strip native binaries#596
Conversation
- Collect platform debug symbols (.pdb/.dSYM/.debug/.dwp) for release artifacts - Strip ELF/Mach-O outputs after symbol extraction (strip -Sx on Mach-O, --strip-unneeded on ELF) - Use llvm-objcopy for ELF debug extraction and llvm-strip for cross targets - Fix universal macOS/iOS stripping and keep release gating on dry-run
There was a problem hiding this comment.
Pull request overview
This pull request modifies the build pipeline to split debug symbols from native binaries and strip them separately. The changes affect the Cargo build profile, dependencies, and GitHub Actions workflows to implement platform-specific symbol extraction and binary stripping.
Changes:
- Modified Cargo.toml to disable stripping and enable packed debug info splitting in the ffi-production profile
- Added embed-resource and related dependencies for Windows resource embedding
- Implemented symbol collection and stripping steps in the build-native workflow for all platforms
- Added package-symbols and create-release jobs to the nuget workflow for distributing debug symbols
Reviewed changes
Copilot reviewed 3 out of 4 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| Cargo.toml | Changed ffi-production profile to disable stripping and enable split-debuginfo=packed |
| Cargo.lock | Added embed-resource and related dependencies (toml, winnow, vswhom, etc.) for Windows builds |
| .github/workflows/nuget.yml | Added package-symbols job to collect symbols into a zip and create-release job to publish GitHub releases with symbol archives |
| .github/workflows/build-native.yml | Added symbol collection and binary stripping steps for all platforms, including universal builds |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
.github/workflows/nuget.yml
Outdated
| The ``Devolutions.Sspi.$PackageVersion.symbols.zip`` archive contains debug symbols for all platforms: | ||
| - Windows: ``.pdb`` files | ||
| - macOS/iOS: ``.dSYM`` bundles | ||
| - Linux/Android: ``.debug`` or ``.dwp`` files |
There was a problem hiding this comment.
The release notes use double backticks (e.g., .pdb, .dSYM) for file extensions. In Markdown, inline code should use single backticks. These should be changed to single backticks for proper formatting:
.pdbinstead of.pdb.dSYMinstead of.dSYM.debugor.dwpinstead of.debugor.dwp
| The ``Devolutions.Sspi.$PackageVersion.symbols.zip`` archive contains debug symbols for all platforms: | |
| - Windows: ``.pdb`` files | |
| - macOS/iOS: ``.dSYM`` bundles | |
| - Linux/Android: ``.debug`` or ``.dwp`` files | |
| The `Devolutions.Sspi.$PackageVersion.symbols.zip` archive contains debug symbols for all platforms: | |
| - Windows: `.pdb` files | |
| - macOS/iOS: `.dSYM` bundles | |
| - Linux/Android: `.debug` or `.dwp` files |
| - name: Strip release binaries | ||
| if: matrix.build == 'release' | ||
| shell: pwsh | ||
| run: | | ||
| $DotNetOs = $Env:DOTNET_OS | ||
| $DotNetRid = $Env:DOTNET_RID | ||
| $LibPrefix = $Env:LIB_PREFIX | ||
| $RuntimePath = Join-Path "dependencies" "runtimes" "$DotNetRid" "native" | ||
|
|
||
| if ($DotNetOs -eq 'win') { | ||
| # Windows: DLL is already separate from PDB, no stripping needed | ||
| Write-Host "Windows binaries are already compact (PDB is separate)" | ||
| } elseif ($DotNetOs -eq 'osx' -or $DotNetOs -eq 'ios') { | ||
| $DylibFile = Join-Path $RuntimePath "${LibPrefix}DevolutionsSspi.dylib" | ||
| if (Test-Path $DylibFile) { | ||
| & strip -Sx $DylibFile | ||
| Write-Host "Stripped dylib: $DylibFile" | ||
| $SizeMB = [math]::Round((Get-Item $DylibFile).Length / 1MB, 2) | ||
| Write-Host "Size after stripping: $SizeMB MB" | ||
| } | ||
| } elseif ($DotNetOs -eq 'android' -or $DotNetOs -eq 'linux') { | ||
| $SoFile = Join-Path $RuntimePath "${LibPrefix}DevolutionsSspi.so" | ||
| if (Test-Path $SoFile) { | ||
| if ($DotNetOs -eq 'android') { | ||
| $StripTool = Join-Path $Env:ANDROID_NDK_HOME "toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip" | ||
| } else { | ||
| $StripTool = 'llvm-strip' | ||
| } | ||
| Write-Host "Using strip tool: $StripTool" | ||
| & $StripTool --strip-unneeded $SoFile 2>&1 | Write-Host | ||
| Write-Host "Stripped .so ($DotNetOs): $SoFile" | ||
| $SizeMB = [math]::Round((Get-Item $SoFile).Length / 1MB, 2) | ||
| Write-Host "Size after stripping: $SizeMB MB" | ||
| } | ||
| } | ||
|
|
||
| - name: Upload native components | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: sspi-${{matrix.os}}-${{matrix.arch}}-${{matrix.build}} | ||
| path: dependencies/runtimes/${{matrix.os}}-${{matrix.arch}} | ||
|
|
||
| - name: Upload debug symbols | ||
| if: matrix.build == 'release' | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: sspi-${{matrix.os}}-${{matrix.arch}}-${{matrix.build}}-symbols | ||
| path: symbols/${{matrix.os}}-${{matrix.arch}} |
There was a problem hiding this comment.
The stripping of binaries happens before uploading them as artifacts. This means the universal build (build-universal job) will receive already-stripped binaries. When it tries to create a universal binary using lipo and then generate dSYM symbols from it, the debug information will not be available because it was stripped out.
To fix this, you should either:
- Upload unstripped binaries and strip them after the universal build creates the universal binary, or
- Create the universal dSYM by combining the arch-specific dSYMs using dsymutil or lipo on the dSYM bundles themselves, rather than trying to extract it from an already-stripped universal binary.
The recommended approach is option 1: move the stripping step to after the upload, and do the stripping in the universal build job after generating the universal dSYM.
| strip = false | ||
| split-debuginfo = "packed" |
There was a problem hiding this comment.
The Cargo profile sets split-debuginfo = "packed", but according to Rust documentation, Linux does not support "packed" as a value for split-debuginfo. Linux only supports "unpacked" and "off". The "packed" option is specific to macOS where it creates .dSYM bundles.
On Linux, this might fall back to a default behavior or cause issues. Consider using a platform-specific configuration or explicitly setting split-debuginfo = "unpacked" for Linux targets to ensure debug information is properly split into separate files.
The code at lines 257-260 expects a .dwp file which might not be generated with the current Cargo profile configuration for Linux builds.
| } | ||
| Write-Host "Extracting debug info with: $ObjCopy --only-keep-debug" | ||
| & $ObjCopy --only-keep-debug $SoFile $DebugOutput 2>&1 | Write-Host |
There was a problem hiding this comment.
For Linux builds, llvm-objcopy is referenced without a full path, relying on it being in the PATH after the LLVM setup step. While this should work if the setup-llvm action properly configures the PATH, it would be more robust to verify the tool exists before use or handle potential errors.
Consider adding error handling to check if the tool is available, similar to how the apple-strip availability is verified in the universal build (lines 435-436, 473-474).
| } | |
| Write-Host "Extracting debug info with: $ObjCopy --only-keep-debug" | |
| & $ObjCopy --only-keep-debug $SoFile $DebugOutput 2>&1 | Write-Host | |
| } | |
| $ObjCopyCmd = Get-Command $ObjCopy -ErrorAction SilentlyContinue | |
| if (-not $ObjCopyCmd) { | |
| Write-Error "llvm-objcopy not found at path or command '$ObjCopy'. Ensure LLVM is installed and available on PATH." | |
| exit 1 | |
| } | |
| Write-Host "Extracting debug info with: $ObjCopy --only-keep-debug" | |
| & $ObjCopy --only-keep-debug $SoFile $DebugOutput 2>&1 | Write-Host | |
| if ($LASTEXITCODE -ne 0) { | |
| Write-Error "llvm-objcopy failed with exit code $LASTEXITCODE while extracting debug info from '$SoFile'." | |
| exit $LASTEXITCODE | |
| } |
|
Addressed the workflow comments: added explicit llvm-objcopy availability checks for Linux/Android extraction and fixed the release notes inline code formatting.\n\nOn split-debuginfo: Linux may or may not emit a .dwp with packed; the workflow now handles both paths (copy .dwp if present, otherwise use llvm-objcopy to emit a .debug file). Cargo profiles are not target-specific on stable, so we keep packed for macOS dSYM behavior and rely on the Linux fallback. |
Summary
Size comparison vs 2026.1.20
Test