Skip to content

ci(nuget): split symbols and strip native binaries#596

Merged
CBenoit merged 2 commits intomasterfrom
feature/nuget-release-workflow
Feb 6, 2026
Merged

ci(nuget): split symbols and strip native binaries#596
CBenoit merged 2 commits intomasterfrom
feature/nuget-release-workflow

Conversation

@mamoreau-devolutions
Copy link
Contributor

Summary

  • Split debug symbols for all platforms and strip native outputs after extraction
  • Use llvm-objcopy/llvm-strip for ELF targets; use apple-strip -Sx for Mach-O
  • Fix universal macOS/iOS stripping and keep release gating on dry-run

Size comparison vs 2026.1.20

  • Windows: no change
  • Linux/Android: no change (now matches old release)
  • macOS/iOS: no change (now matches old release)

Test

  • GH Actions nuget.yml dry-run

- 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
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines 273 to 276
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
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:

  • .pdb instead of .pdb
  • .dSYM instead of .dSYM
  • .debug or .dwp instead of .debug or .dwp
Suggested change
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

Copilot uses AI. Check for mistakes.
Comment on lines +282 to +329
- 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}}
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:

  1. Upload unstripped binaries and strip them after the universal build creates the universal binary, or
  2. 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.

Copilot uses AI. Check for mistakes.
Comment on lines +242 to +243
strip = false
split-debuginfo = "packed"
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment on lines +267 to +269
}
Write-Host "Extracting debug info with: $ObjCopy --only-keep-debug"
& $ObjCopy --only-keep-debug $SoFile $DebugOutput 2>&1 | Write-Host
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Suggested change
}
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
}

Copilot uses AI. Check for mistakes.
@mamoreau-devolutions
Copy link
Contributor Author

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.

Copy link
Member

@CBenoit CBenoit left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@CBenoit CBenoit merged commit 0d0ac42 into master Feb 6, 2026
98 checks passed
@CBenoit CBenoit deleted the feature/nuget-release-workflow branch February 6, 2026 17:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants