Skip to content
Open
Show file tree
Hide file tree
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
6 changes: 6 additions & 0 deletions .pipelines/v2/templates/stages-build-native.yml
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,9 @@ stages:
- output: pipelineArtifact
artifactName: 'cpp-native-linux-x64'
targetPath: '$(Build.ArtifactStagingDirectory)/native'
- output: pipelineArtifact
artifactName: 'cpp-native-symbols-linux-x64'
targetPath: '$(Build.ArtifactStagingDirectory)/symbols'
steps:
- template: steps-build-linux.yml
parameters:
Expand Down Expand Up @@ -139,6 +142,9 @@ stages:
- output: pipelineArtifact
artifactName: 'cpp-native-osx-arm64'
targetPath: '$(Build.ArtifactStagingDirectory)/native'
- output: pipelineArtifact
artifactName: 'cpp-native-symbols-osx-arm64'
targetPath: '$(Build.ArtifactStagingDirectory)/symbols'
steps:
- template: steps-build-macos.yml
parameters:
Expand Down
17 changes: 17 additions & 0 deletions .pipelines/v2/templates/steps-build-linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -96,4 +96,21 @@ steps:
fi
cp -P "$primary" "$dst/"
echo " staged libfoundry_local.so"

displayName: 'Stage native artifacts'

# Stage debug symbols into a separate artifact so they can be published
# independently without being bundled into the Python wheel (which consumes
# the native artifact wholesale and must not contain the .dbg file).
- bash: |
set -euo pipefail
src='$(Build.SourcesDirectory)/sdk_v2/cpp/build/Linux/${{ parameters.buildConfig }}/bin'
dst='$(Build.ArtifactStagingDirectory)/symbols'
mkdir -p "$dst"

dbg="$src/libfoundry_local.so.dbg"
if [ -f "$dbg" ]; then
cp -P "$dbg" "$dst/"
echo " staged libfoundry_local.so.dbg"
Comment thread
skottmckay marked this conversation as resolved.
fi
displayName: 'Stage debug symbol artifacts'
19 changes: 19 additions & 0 deletions .pipelines/v2/templates/steps-build-macos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -101,4 +101,23 @@ steps:
fi
cp -P "$primary" "$dst/"
echo " staged libfoundry_local.dylib"

displayName: 'Stage native artifacts'

# Stage debug symbols into a separate artifact so they can be published
# independently without being bundled into the Python wheel (which consumes
# the native artifact wholesale and must not contain the .dSYM bundle;
# the bundle is a directory whose inner DWARF file shares the name
# libfoundry_local.dylib and would corrupt the wheel if copied flat).
- bash: |
set -euo pipefail
src='$(Build.SourcesDirectory)/sdk_v2/cpp/build/macOS/${{ parameters.buildConfig }}/bin'
dst='$(Build.ArtifactStagingDirectory)/symbols'
mkdir -p "$dst"

dsym="$src/libfoundry_local.dylib.dSYM"
if [ -d "$dsym" ]; then
cp -r "$dsym" "$dst/"
echo " staged libfoundry_local.dylib.dSYM"
Comment on lines +118 to +121
fi
displayName: 'Stage debug symbol artifacts'
5 changes: 5 additions & 0 deletions sdk_v2/cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,11 @@ target_include_directories(foundry_local_cpp
)
target_link_libraries(foundry_local_cpp INTERFACE foundry_local Microsoft.GSL::GSL)

# --------------------------------------------------------------------------
# Split debug symbols (Linux / macOS, RelWithDebInfo only)
# --------------------------------------------------------------------------
include(cmake/SplitDebugSymbols.cmake)

# --------------------------------------------------------------------------
# Tests
# --------------------------------------------------------------------------
Expand Down
111 changes: 111 additions & 0 deletions sdk_v2/cpp/cmake/SplitDebugSymbols.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# Copyright (c) Microsoft. All rights reserved.
#
# Splits debug symbols from the foundry_local shared library for RelWithDebInfo
# builds on Linux and macOS.
#
# Linux: objcopy --only-keep-debug + strip --strip-debug +
# objcopy --add-gnu-debuglink wires the stripped .so back to its .dbg
# companion. GDB/LLDB find the .dbg automatically via the GNU
# debuglink section — no user configuration needed.
#
# macOS: dsymutil harvests DWARF from the .o debug maps into a .dSYM bundle,
# then strip -S removes the N_OSO stab entries from the .dylib.
# LLDB/Xcode find the .dSYM automatically when it sits in the same
# directory as the .dylib.
#
# Note: without this step the current macOS build has no persistent
# debug info — DWARF lives only in .o files that are gone after a
# clean or on any consumer machine. dsymutil is required.
#
# The split happens at build time so local developers get the same layout as CI
# without any manual steps; GDB/LLDB discover the symbols automatically.
#
# This is a no-op for:
# Debug — symbols are intentionally embedded, stripping defeats the purpose.
# Release — compiled without -g, nothing to extract.

if(NOT (CMAKE_SYSTEM_NAME STREQUAL "Linux" OR APPLE))
return()
endif()

# Single-config generators (Ninja/Makefile) set CMAKE_BUILD_TYPE at configure
# time, which is how both the Linux and macOS CI agents invoke build.py. Only
# wire up the split for RelWithDebInfo.
if(NOT CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
return()
endif()

# --------------------------------------------------------------------------
# macOS: dsymutil → .dSYM bundle, then strip -S
# --------------------------------------------------------------------------
if(APPLE)
find_program(DSYMUTIL dsymutil)
if(NOT DSYMUTIL)
message(WARNING
"dsymutil not found — dSYM generation disabled for RelWithDebInfo. "
"Install Xcode Command Line Tools to enable debug symbol extraction.")
return()
endif()

# dsymutil must run BEFORE strip: it reads the N_OSO stab entries that
# point to .o files to harvest their DWARF. strip -S then removes those
# stab entries from the .dylib, leaving a clean production binary.
add_custom_command(TARGET foundry_local POST_BUILD
COMMAND "${DSYMUTIL}"
"$<TARGET_FILE:foundry_local>"
-o "$<TARGET_FILE:foundry_local>.dSYM"
COMMAND strip
-S
"$<TARGET_FILE:foundry_local>"
COMMENT "Generating dSYM bundle and stripping debug map from libfoundry_local.dylib"
VERBATIM
)
message(STATUS "RelWithDebInfo: dSYM extraction + strip configured for libfoundry_local.dylib")

# --------------------------------------------------------------------------
# Linux: objcopy --only-keep-debug + strip --strip-debug + --add-gnu-debuglink
# --------------------------------------------------------------------------
elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
# Prefer the toolchain's objcopy/strip for cross-compilation correctness
# (CMAKE_OBJCOPY / CMAKE_STRIP are set by CMake when it detects the
# compiler). Fall back to find_program for environments that don't have
# these variables populated.
set(_objcopy "${CMAKE_OBJCOPY}")
if(NOT _objcopy)
find_program(_objcopy objcopy)
endif()

set(_strip "${CMAKE_STRIP}")
if(NOT _strip)
find_program(_strip strip)
endif()

if(NOT _objcopy OR NOT _strip)
message(WARNING
"objcopy/strip not found — debug symbol splitting disabled for RelWithDebInfo. "
"Install binutils to enable symbol extraction.")
return()
endif()

add_custom_command(TARGET foundry_local POST_BUILD
# 1. Extract DWARF sections into a companion .dbg file.
COMMAND "${_objcopy}"
--only-keep-debug
"$<TARGET_FILE:foundry_local>"
"$<TARGET_FILE:foundry_local>.dbg"
# 2. Strip DWARF from the .so. --strip-debug keeps the exported symbol
# table (required for dynamic linking) but removes .debug_* sections.
COMMAND "${_strip}"
--strip-debug
"$<TARGET_FILE:foundry_local>"
# 3. Embed a GNU debuglink section so GDB/LLDB auto-discover the .dbg
# file. The debuglink stores the basename; GDB searches the same
# directory as the .so first, so colocating them is sufficient.
COMMAND "${_objcopy}"
"--add-gnu-debuglink=$<TARGET_FILE:foundry_local>.dbg"
"$<TARGET_FILE:foundry_local>"
COMMENT "Splitting debug symbols into libfoundry_local.so.dbg"
VERBATIM
)
message(STATUS "RelWithDebInfo: debug symbol splitting configured for libfoundry_local.so")
endif()