Xcode 12 beta 3 – XCFrameworks and debug symbols

Xcode 12 beta 3 introduces the new -debug-symbols option for xcodebuild -create-xcframework.

After testing, I was unable to use that option correctly.

I setup a framework target with a single struct in it for testing purposes. I create an xcarchive of the framework this way:
Code Block sh
$ xcodebuild -scheme "BrazeKit" -configuration "Release" -sdk "iphones" -archivePath ./.build/xcarchives/BrazeKit/iOS/iphoneos archive

This generates the expected ./.build/xcarchives/BrazeKit/iOS/iphoneos.xcarchive which contains:
Code Block sh
.
├── BCSymbolMaps
│   ├── 0A25AE50-A721-30A0-BC09-E8CFA503D4A6.bcsymbolmap
│   ├── 13B3D8E5-9D37-300A-8EB9-72A33AD89E26.bcsymbolmap
│   ├── 7D10C250-E408-334E-92FC-3444B7D0E067.bcsymbolmap
│   └── AF8424FB-543E-37E1-A7BF-64DE4BCABD39.bcsymbolmap
├── dSYMs
│ └── BrazeKit.framework.dSYM
│ └── Contents
│ ├── Info.plist
│ └── Resources
│ └── DWARF
│ └── BrazeKit
└── # rest omitted for the sake of brevity


I can build an XCFramework from that archive with:
Code Block sh
$ xcodebuild -create-xcframework -framework ./.build/xcarchives/BrazeKit/iOS/iphoneos.xcarchive/Products/Library/Frameworks/BrazeKit.framework -output BrazeKit.xcframework


But I am unable to attach any debug symbols using -debug-symbols:
Code Block sh
$ xcodebuild -create-xcframework -framework ./.build/xcarchives/BrazeKit/iOS/iphoneos.xcarchive/Products/Library/Frameworks/BrazeKit.framework -debug-symbols ./build/xcarchives/BrazeKit/iOS/iphoneos.xcarchive/dSYMs/BrazeKit.framework.dSYM -output BrazeKit.xcframework
error: the path does not point to a valid debug symbols file: ./.build/xcarchives/BrazeKit/iOS/iphoneos.xcarchive/dSYMs/BrazeKit.framework.dSYM
# Using the dSYM package BrazeKit.framework.dSYM

Code Block sh
$ xcodebuild -create-xcframework -framework ./.build/xcarchives/BrazeKit/iOS/iphoneos.xcarchive/Products/Library/Frameworks/BrazeKit.framework -debug-symbols ./.build/xcarchives/BrazeKit/iOS/iphoneos.xcarchive/dSYMs/BrazeKit.framework.dSYM/Contents/Resources/DWARF/BrazeKit -output BrazeKit.xcframework
error: the path does not point to a valid debug symbols file: ./.build/xcarchives/BrazeKit/iOS/iphoneos.xcarchive/dSYMs/BrazeKit.framework.dSYM/Contents/Resources/DWARF/BrazeKit
# Using the file under the DWARF directory in the dSYM package


Whichever dSYM file I pass, I always get the error the path does not point to a valid debug symbols file.

Could you provide an example on how to correctly use the option -debug-symbols?

Thanks,
Your build commands have a few mistakes. I made the same simple framework you did (a single Swift struct), and used the following build commands instead. I'm going to format them here with line breaks and indentation for readability. Note that I've set Build Library for Distribution to Yes and Skip Install to No in the framework target's build settings, so they don't appear here.

Code Block
% xcodebuild archive -scheme StructKit
-destination "generic/platform=iOS"
-archivePath iOS.xcarchive
% xcodebuild archive -scheme StructKit
-destination "generic/platform=iOS Simulator"
-archivePath sim.xcarchive
% xcodebuild -create-xcframework -framework iOS.xcarchive/Products/Library/Frameworks/StructKit.framework
-debug-symbols iOS.xcarchive/dSYMs/StructKit.framework.dSYM
-debug-symbols iOS.xcarchive/BCSymbolMaps/<UUID>.bcsymbolmap
-framework sim.xcarchive/Products/Library/Frameworks/StructKit.framework
-debug-symbols sim.xcarchive/dSYMs/StructKit.framework.dSYM
-output StructKit.xcframework


I also notice you have 4 bcsymbolmap files -- you should only have 1 or 2, depending on the CPU architectures the framework supports on device. It's possible there's a target configuration issue in your project, and you're pulling in the wrong CPU architectures for the iOS build.
@edford in your example, you have <UUID>.bcsymbolmap representing that the UUID value needs to be provided. I did a quick test and verified that I'm not able to just pass the path to the BCSymbolMaps directory, but rather need to pass the path to a specific .bcsymbolmap file.

Since the UUID value changes between builds, it would be ideal to have an easy way to pass anything in the directory. Do you know of any way to specify a wildcard that would grab everything in the directory, or would we need to setup separate logic to dynamically get the list of available bcsymbolmap files and build up an xcodebuild command that uses them explicitly?
I'd be explicit and name each file right now. You raise a good point about needing to add scripting around discovery for each file because of the build UUID naming convention, rather than just giving it a directory or a wildcard, so you should create a Feedback Assistant report so that the Xcode engineers can consider adding this. Please post the FB number here so we can cross-reference it.
Thanks @edford. I've submitted Feedback Assistant report FB8339148 and have linked to this thread from the ticket.
I have the same issue here regarding the path of dSYMs and BCSymbolMaps. Even when I specify the path to the dSYM for my framework, I get this error:

error: the path does not point to a valid debug symbols file:

Hi all,

I came across the same issue when adding -debug-symbols to xcframework, which resulted with following error:
Code Block
error: the path does not point to a valid debug symbols


I resolved it by using the absolute path when referencing the dSYM/BCSymbolmap files

So instead of using the relative path when referencing eg dSYM file
Code Block
xcodebuild -create-xcframework
-framework myFramework-iOS.xcarchive/Products/Library/Frameworks/myFramework.framework
-debug-symbols myFramework-iOS.xcarchive/dSYMs/myFramework.framework.dSYM
...


I would use the absolute path
Code Block
xcodebuild -create-xcframework
-framework myFramework-iOS.xcarchive/Products/Library/Frameworks/myFramework.framework
-debug-symbols ~/myFolder/myFramework-iOS.xcarchive/dSYMs/myFramework.framework.dSYM
...


This way, the dSYM was properly integrated into final xcframework. Hope this helps somebody.
@Edford I think the issue here is that the framework might have 3rd party dependencies (like mine), so the bcsymbolmap files that are produced are more than 2. This way we can't distinguish which are which and for sure there is no automated way to include the right ones in the xcframework.

EDIT: I can confirm it worked for me too to include the dsym files by specifying the full path (from ~/...)
For those who (like me) stumbled upon this thread trying to create an XCFramework with debug symbols & bitcode, I've made a build-phase script that makes it easy to implement.
  1. Create a new target for your framework that is just for creating the .xcframework container (this is needed because when running this script on the same target, it gets an infinite loop)

  2. Add the script to the target's Build phases --> Add new run script

  3. For the rest, see the comments in the script

Code Block
# Set the output folder var
UNIVERSAL_OUTPUTFOLDER=${BUILD_DIR}/${CONFIGURATION}-universal
# To make an XCFramework, we first build the framework for every type seperately
echo "XCFramework: Starting script to build an XCFramework. Output dir: ${UNIVERSAL_OUTPUTFOLDER}"
# Device slice.
echo "XCFramework: Archiving DEVICE type..."
xcodebuild archive -scheme "${PROJECT_NAME}" -configuration Release -destination 'generic/platform=iOS' -archivePath "${BUILD_DIR}/archives/${PROJECT_NAME}.framework-iphoneos.xcarchive" SKIP_INSTALL=NO
echo "XCFramework: Archiving SIMULATOR type..."
# Simulator slice.
xcodebuild archive -scheme "${PROJECT_NAME}" -configuration Release -destination 'generic/platform=iOS Simulator' -archivePath "${BUILD_DIR}/archives/${PROJECT_NAME}.framework-iphonesimulator.xcarchive" SKIP_INSTALL=NO
# First, get all the UUID filepaths for BCSymbolMaps, because these are randomly generated and need to be individually added as the `-debug-symbols` parameter. The dSYM path is always the same so that one is manually added
echo "XCFramework: Generating IPHONE BCSymbolMap paths..."
IPHONE_BCSYMBOLMAP_PATHS=(${BUILD_DIR}/archives/${PROJECT_NAME}.framework-iphoneos.xcarchive/BCSymbolMaps/*)
IPHONE_BCSYMBOLMAP_COMMANDS=""
for path in "${IPHONE_BCSYMBOLMAP_PATHS[@]}"; do
  IPHONE_BCSYMBOLMAP_COMMANDS="$IPHONE_BCSYMBOLMAP_COMMANDS -debug-symbols $path "
  echo $IPHONE_BCSYMBOLMAP_COMMANDS
done
# Simulator-targeted archives don't generate BCSymbolMap files, so above is only needed for iphone target
echo "XCFramework: Creating XCFramework file"
# Then we group them into one XCFramework file
xcodebuild -create-xcframework -framework "${BUILD_DIR}/archives/${PROJECT_NAME}.framework-iphoneos.xcarchive/Products/Library/Frameworks/${PROJECT_NAME}.framework" -debug-symbols "${BUILD_DIR}/archives/${PROJECT_NAME}.framework-iphoneos.xcarchive/dSYMs/${PROJECT_NAME}.framework.dSYM" $IPHONE_BCSYMBOLMAP_COMMANDS -framework "${BUILD_DIR}/archives/${PROJECT_NAME}.framework-iphonesimulator.xcarchive/Products/Library/Frameworks/${PROJECT_NAME}.framework" -debug-symbols "${BUILD_DIR}/archives/${PROJECT_NAME}.framework-iphonesimulator.xcarchive/dSYMs/${PROJECT_NAME}.framework.dSYM" -output "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.xcframework"
# For developer convenience, open the output folder
open "${UNIVERSAL_OUTPUTFOLDER}"

Thanks for solution, @Svantulden.
Using your approach, I was able to generate XCFramework. Unfortunately, there are issues with archiving project which uses such XCFramework. Archiving itself does not fail, but it produces corrupted XCArchive, which is not possible to open in Xcode or export via command-line (it fails with archive at path is malformed message).
This is my solution based on approach used by PSPDFKit. Hope it can help somebody.

Code Block #!/bin/bash
WORKSPACE="MY_WORKSPACE.xcworkspace"
SCHEME="MY_SCHEME"
FRAMEWORK_NAME="MY_FRAMEWORK_NAME"
IPHONEOS_ARCHIVE_PATH="./build/${FRAMEWORK_NAME}-iphoneos.xcarchive"
IPHONESIMULATOR_ARCHIVE_PATH="./build/${FRAMEWORK_NAME}-iphonesimulator.xcarchive"
OUTPUT_PATH="./build/${FRAMEWORK_NAME}.xcframework"
rm -rf ${OUTPUT_PATH}
set -o pipefail
# Device slice.
xcodebuild clean archive \
-workspace ${WORKSPACE} \
-scheme ${SCHEME} \
-configuration Release \
-sdk iphoneos \
-destination 'generic/platform=iOS' \
-archivePath ${IPHONEOS_ARCHIVE_PATH} \
SKIP_INSTALL=NO \
BUILD_LIBRARIES_FOR_DISTRIBUTION=YES | xcpretty
# Simulator slice.
xcodebuild clean archive \
-workspace ${WORKSPACE} \
-scheme ${SCHEME} \
-configuration Release \
-sdk iphonesimulator \
-destination 'generic/platform=iOS Simulator' \
-archivePath ${IPHONESIMULATOR_ARCHIVE_PATH} \
SKIP_INSTALL=NO \
BUILD_LIBRARIES_FOR_DISTRIBUTION=YES | xcpretty
function GetUUID() {
# dwarfdump output:
# UUID: FFFFFFF-AAAAA-BBBB-CCCC-DDDDDDDDDD (arm64) PATH_TO_ARCHIVE/FRAMEWORK.framework-ios-arm64.xcarchive/Products/Library/Frameworks/FRAMEWORK.framework/FRAMEWORK
local arch=$1
local binary=$2
local dwarfdump_result=$(dwarfdump -u ${binary})
local regex="UUID: (.*) \((.*)\)"
if [[ $dwarfdump_result =~ $regex ]]; then
local result_uuid="${BASH_REMATCH[1]}"
local result_arch="${BASH_REMATCH[2]}"
if [ "$result_arch" == "$arch" ]; then
echo $result_uuid
fi
fi
}
# First, find UUID for BCSymbolMaps of our binary, because these are randomly generated. The dSYM path is always the same so that one is manually added
# Simulator-targeted archives don't generate BCSymbolMap files, so this is only needed for iphone target
BCSYMBOLMAP_UUID=$(GetUUID "arm64" "${IPHONEOS_ARCHIVE_PATH}/Products/Library/Frameworks/${FRAMEWORK_NAME}.framework/${FRAMEWORK_NAME}")
# Create XCFramework
xcodebuild -create-xcframework \
-framework "${IPHONEOS_ARCHIVE_PATH}/Products/Library/Frameworks/${FRAMEWORK_NAME}.framework" \
-debug-symbols "${PWD}/${IPHONEOS_ARCHIVE_PATH}/dSYMs/${FRAMEWORK_NAME}.framework.dSYM" \
-debug-symbols "${PWD}/${IPHONEOS_ARCHIVE_PATH}/BCSymbolMaps/${BCSYMBOLMAP_UUID}.bcsymbolmap" \
-framework "${IPHONESIMULATOR_ARCHIVE_PATH}/Products/Library/Frameworks/${FRAMEWORK_NAME}.framework" \
-debug-symbols "${PWD}/${IPHONESIMULATOR_ARCHIVE_PATH}/dSYMs/${FRAMEWORK_NAME}.framework.dSYM" \
-output ${OUTPUT_PATH}
# Cleanup
rm -rf "${IPHONEOS_ARCHIVE_PATH}"
rm -rf "${IPHONESIMULATOR_ARCHIVE_PATH}"


Just wanted to add a bit to this since I came to this thread looking to resolve why my framework builds did not include simulator binaries for both M1 Macs (ARM) and Intel.

I was using the same "xcodebuild archive -scheme StructKit -destination "generic/platform=iOS Simulator" ..." command mentioned before, however for me I only ever saw an intel binary in the resigning framework generated.

What ended up being the fix, was that inside my .xcproj file some old version of Xcode has left a default "archs" value for simulators that looked like this:

VALID_ARCHS[sdk=iphonesimulator]

I removed those from two places in the .xcodeproj file, and after that the xcodebuild command produced a framework that had a fat binary with both ARM and Intel.

I'm successfully adding the .dSYM to the XCFramework, but are the .bcsymbolmap files needed?

I try to insert "-allow-internal-distribution" into the cmd worked at last.

Xcode 12 beta 3 – XCFrameworks and debug symbols
 
 
Q