Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
0f3e065
Prototype: SPM distribution via prebuilt xcframework (Apple)
bmehta001 Jun 18, 2026
5e3687e
Prototype: SPM release workflow + parallel 3-component SemVer tag
bmehta001 Jun 18, 2026
ba7ba4c
Fix local SPM xcframework consumption
bmehta001 Jun 18, 2026
308e031
Copy only public ObjC headers into xcframework
bmehta001 Jun 18, 2026
dd97234
Align SPM package with xcframework contents
bmehta001 Jun 18, 2026
accb600
Generate SPM availability from xcframework build
bmehta001 Jun 18, 2026
4e03dff
Address SPM prototype review refinements
bmehta001 Jun 18, 2026
0edd5f1
Align Apple SPM docs and module availability
bmehta001 Jun 18, 2026
6d27fba
Add macOS slice to SPM xcframework
bmehta001 Jun 19, 2026
d242830
Add Mac Catalyst slice to SPM xcframework
bmehta001 Jun 19, 2026
1d89f6d
Add visionOS slices to SPM xcframework
bmehta001 Jun 19, 2026
0246847
Address SPM release review comments
bmehta001 Jun 19, 2026
9634894
Address follow-up SPM review comments
bmehta001 Jun 19, 2026
5923e5c
Validate SPM Apple platforms in release workflow
bmehta001 Jun 19, 2026
ccceaa5
Skip package creation for xcframework slices
bmehta001 Jun 19, 2026
5c6f574
Clean up Apple SPM README
bmehta001 Jun 19, 2026
79f55fa
Remove status label from Apple SPM README
bmehta001 Jun 19, 2026
27049fe
Assert SPM platforms in release workflow
bmehta001 Jun 19, 2026
55c4ce5
Clean up xcframework build script comments
bmehta001 Jun 19, 2026
9a18330
Use fresh build cache for each xcframework slice
bmehta001 Jun 19, 2026
279ffd3
Use portable shell comparison in iOS build
bmehta001 Jun 19, 2026
7bc7415
Quote ${IOS_PLAT}/${IOS_ARCH} in Apple if() conditions
bmehta001 Jun 19, 2026
9d52266
Rename public Clang module ObjCModule -> MATTelemetryObjC
bmehta001 Jun 19, 2026
2b5bdd5
docs(apple): note xcframework expects system sqlite3/zlib
bmehta001 Jun 19, 2026
f2c5fc9
Use PIIKind alias in SemanticContext.setUserID signature
bmehta001 Jun 19, 2026
06caea2
Fix SemanticContext.setUserID dropping the caller's piiKind
bmehta001 Jun 19, 2026
a57c2a1
Address Swift package review refinements
bmehta001 Jun 19, 2026
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
172 changes: 172 additions & 0 deletions .github/workflows/spm-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
name: SPM release (xcframework)

# On a published SDK release, build MATTelemetry.xcframework, upload it to the
# GitHub Release, and publish a 3-component SemVer tag for Swift Package Manager
# whose Package.swift binaryTarget points at the uploaded artifact + checksum.
#
# Why a separate tag: the SDK's own release tags are 4-component (vX.Y.Z.W),
# which is NOT valid SemVer, so Swift Package Manager ignores them. This derives
# a 3-component tag (X.Y.Z) from the same release that SPM can resolve.
#
# Prerequisites:
# * The root Package.swift (the SPM manifest) must exist at the release tag
# (i.e. this prototype merged to main before the release is cut).
# * Uses the default GITHUB_TOKEN (needs contents: write). No extra secrets.

on:
release:
types: [published]
workflow_dispatch:
inputs:
tag:
description: "4-component release tag to publish for SPM (e.g. v3.10.161.1)"
required: true
type: string

permissions:
contents: write

concurrency:
group: spm-release-${{ github.event.release.tag_name || inputs.tag }}
cancel-in-progress: false

jobs:
spm:
name: Publish SPM xcframework + tag
# Skip drafts/pre-releases; always allow manual dispatch.
if: >-
${{ github.event_name == 'workflow_dispatch' ||
(github.event.release.draft == false && github.event.release.prerelease == false) }}
runs-on: macos-15 # provides Xcode with Apple platform SDKs (xcodebuild, swift)
env:
ARTIFACT: MATTelemetry.xcframework.zip
steps:
- name: Resolve tag and derive SPM version
id: ver
env:
# Pass untrusted tag values through the environment rather than
# interpolating ${{ ... }} into the script body.
RELEASE_TAG: ${{ github.event.release.tag_name }}
INPUT_TAG: ${{ inputs.tag }}
run: |
set -euo pipefail
TAG="${RELEASE_TAG:-$INPUT_TAG}"
if [ -z "$TAG" ]; then echo "::error::No release tag could be resolved."; exit 1; fi
# Only act on 4-component version tags vX.Y.Z.W.
if ! printf '%s' "$TAG" | grep -Eq '^v[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$'; then
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
echo "::error::Tag '$TAG' is not a 4-component version tag (expected vX.Y.Z.W)."
exit 1
fi
echo "::notice::Tag '$TAG' is not a 4-component version tag; nothing to publish."
echo "skip=true" >> "$GITHUB_OUTPUT"
exit 0
fi
VERSION="${TAG#v}" # X.Y.Z.W
SPM_VERSION="${VERSION%.*}" # X.Y.Z (drop the trailing build component)
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
echo "spm_version=$SPM_VERSION" >> "$GITHUB_OUTPUT"
echo "Release $TAG -> SPM tag $SPM_VERSION"

- name: Checkout the release tag
if: ${{ steps.ver.outputs.skip != 'true' }}
uses: actions/checkout@v4
with:
ref: ${{ steps.ver.outputs.tag }}
fetch-depth: 0
# The private lib/modules submodule is intentionally NOT fetched; the
# xcframework ships the core SDK + Obj-C wrappers, matching the vcpkg
# port (the optional modules are excluded there too).
submodules: false

- name: Build MATTelemetry.xcframework
if: ${{ steps.ver.outputs.skip != 'true' }}
run: |
set -euo pipefail
chmod +x tools/apple/build-xcframework.sh
tools/apple/build-xcframework.sh release
test -f "build/apple/$ARTIFACT"

- name: Validate SwiftPM package consumption
if: ${{ steps.ver.outputs.skip != 'true' }}
run: |
set -euo pipefail
swift package dump-package > package.json
python3 - <<'PY'
import json
expected = {
"ios": "12.0",
"maccatalyst": "14.0",
"macos": "10.15",
"visionos": "1.0",
}
with open("package.json", encoding="utf-8") as f:
platforms = {
item["platformName"]: item["version"]
for item in json.load(f)["platforms"]
}
if platforms != expected:
raise SystemExit(f"Unexpected Package.swift platforms: {platforms}")
PY
swift build
xcodebuild -scheme OneDSSwift -destination 'generic/platform=iOS Simulator' build
xcodebuild -scheme OneDSSwift -destination 'platform=macOS,variant=Mac Catalyst' build
xcodebuild -scheme OneDSSwift -destination 'generic/platform=visionOS Simulator' build

- name: Compute SPM checksum
id: sum
if: ${{ steps.ver.outputs.skip != 'true' }}
run: |
set -euo pipefail
checksum="$(swift package compute-checksum "build/apple/$ARTIFACT")"
echo "checksum=$checksum" >> "$GITHUB_OUTPUT"

- name: Upload xcframework to the release
if: ${{ steps.ver.outputs.skip != 'true' }}
env:
GH_TOKEN: ${{ github.token }}
run: gh release upload "${{ steps.ver.outputs.tag }}" "build/apple/$ARTIFACT" --clobber

- name: Point Package.swift at the released artifact
if: ${{ steps.ver.outputs.skip != 'true' }}
env:
ASSET_URL: https://github.com/${{ github.repository }}/releases/download/${{ steps.ver.outputs.tag }}/MATTelemetry.xcframework.zip
CHECKSUM: ${{ steps.sum.outputs.checksum }}
run: |
set -euo pipefail
python3 - "$ASSET_URL" "$CHECKSUM" <<'PY'
import re, sys
url, checksum = sys.argv[1], sys.argv[2]
path = "Package.swift"
src = open(path).read()
repl = (
'.binaryTarget(\n'
' name: "MATTelemetry",\n'
f' url: "{url}",\n'
f' checksum: "{checksum}")'
)
out = re.sub(
r'\.binaryTarget\(\s*name:\s*"MATTelemetry",\s*path:\s*"[^"]*"\s*\)',
repl, src, count=1)
assert out != src, "binaryTarget(path:) block not found in Package.swift"
open(path, "w").write(out)
PY

- name: Commit manifest and push the 3-component SPM tag
if: ${{ steps.ver.outputs.skip != 'true' }}
run: |
set -euo pipefail
if git ls-remote --exit-code --tags origin "refs/tags/${{ steps.ver.outputs.spm_version }}" >/dev/null; then
echo "::notice::SPM tag ${{ steps.ver.outputs.spm_version }} already exists; skipping tag publish."
exit 0
fi
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add Package.swift tools/apple/MATTelemetryAvailability.json
git commit -m "[spm] ${{ steps.ver.outputs.spm_version }}: pin xcframework url + checksum"
# The SPM tag points at this commit (release source + resolved
# binaryTarget). It is published as a tag only, not merged to a branch.
git tag -a "${{ steps.ver.outputs.spm_version }}" \
-m "Swift Package Manager release ${{ steps.ver.outputs.spm_version }} (from ${{ steps.ver.outputs.tag }})"
git push origin "refs/tags/${{ steps.ver.outputs.spm_version }}"
echo "Published SPM tag ${{ steps.ver.outputs.spm_version }}"
29 changes: 23 additions & 6 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -54,30 +54,42 @@ if(APPLE)

if(FORCE_RESET_OSX_DEPLOYMENT_TARGET)
set(CMAKE_OSX_DEPLOYMENT_TARGET "" CACHE STRING "Force unset of the deployment target for iOS" FORCE)
if (${IOS_PLAT} STREQUAL "iphonesimulator")
if ("${IOS_PLAT}" STREQUAL "iphonesimulator")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mios-simulator-version-min=${IOS_DEPLOYMENT_TARGET}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mios-simulator-version-min=${IOS_DEPLOYMENT_TARGET}")
else()
elseif("${IOS_PLAT}" STREQUAL "iphoneos")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -miphoneos-version-min=${IOS_DEPLOYMENT_TARGET}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -miphoneos-version-min=${IOS_DEPLOYMENT_TARGET}")
endif()
endif()

if((${IOS_PLAT} STREQUAL "iphoneos") OR (${IOS_PLAT} STREQUAL "iphonesimulator") OR (${IOS_PLAT} STREQUAL "xros") OR (${IOS_PLAT} STREQUAL "xrsimulator"))
if("${IOS_PLAT}" STREQUAL "maccatalyst")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -target ${IOS_ARCH}-apple-ios${IOS_DEPLOYMENT_TARGET}-macabi")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -target ${IOS_ARCH}-apple-ios${IOS_DEPLOYMENT_TARGET}-macabi")
set(IOS_PLATFORM "macosx")
elseif("${IOS_PLAT}" STREQUAL "xros")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -target ${IOS_ARCH}-apple-xros${IOS_DEPLOYMENT_TARGET}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -target ${IOS_ARCH}-apple-xros${IOS_DEPLOYMENT_TARGET}")
set(IOS_PLATFORM "${IOS_PLAT}")
elseif("${IOS_PLAT}" STREQUAL "xrsimulator")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -target ${IOS_ARCH}-apple-xros${IOS_DEPLOYMENT_TARGET}-simulator")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -target ${IOS_ARCH}-apple-xros${IOS_DEPLOYMENT_TARGET}-simulator")
set(IOS_PLATFORM "${IOS_PLAT}")
elseif(("${IOS_PLAT}" STREQUAL "iphoneos") OR ("${IOS_PLAT}" STREQUAL "iphonesimulator"))
set(IOS_PLATFORM "${IOS_PLAT}")
else()
message(FATAL_ERROR "Unrecognized iOS platform '${IOS_PLAT}'")
endif()

if(${IOS_ARCH} STREQUAL "x86_64")
if("${IOS_ARCH}" STREQUAL "x86_64")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -arch x86_64")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -arch x86_64")
set(CMAKE_SYSTEM_PROCESSOR x86_64)
elseif(${IOS_ARCH} STREQUAL "arm64")
elseif("${IOS_ARCH}" STREQUAL "arm64")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -arch arm64")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -arch arm64")
set(CMAKE_SYSTEM_PROCESSOR arm64)
elseif(${IOS_ARCH} STREQUAL "arm64e")
elseif("${IOS_ARCH}" STREQUAL "arm64e")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -arch arm64e")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -arch arm64e")
set(CMAKE_SYSTEM_PROCESSOR arm64e)
Expand All @@ -89,6 +101,11 @@ if(APPLE)
OUTPUT_VARIABLE CMAKE_OSX_SYSROOT
ERROR_QUIET
OUTPUT_STRIP_TRAILING_WHITESPACE)
if("${IOS_PLAT}" STREQUAL "maccatalyst")
set(IOS_SUPPORT_FRAMEWORKS "${CMAKE_OSX_SYSROOT}/System/iOSSupport/System/Library/Frameworks")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -iframework ${IOS_SUPPORT_FRAMEWORKS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -iframework ${IOS_SUPPORT_FRAMEWORKS}")
endif()
message(STATUS "CMAKE_OSX_SYSROOT ${CMAKE_OSX_SYSROOT}")
message(STATUS "ARCHITECTURE: ${CMAKE_SYSTEM_PROCESSOR}")
message(STATUS "PLATFORM: ${IOS_PLATFORM}")
Expand Down
129 changes: 129 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
// swift-tools-version: 5.9
//
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
//
// Swift Package Manager manifest for the 1DS C++ SDK (Microsoft Applications
// Telemetry) on Apple platforms.
//
// PROTOTYPE — distribution model:
// * The compiled C++ core + Obj-C wrappers ship as a prebuilt binary
// xcframework (built by tools/apple/build-xcframework.sh). This avoids
// compiling the CMake/Bond/sqlite/zlib C++ tree through SPM, which is not
// practical.
// * The thin Swift wrapper (wrappers/swift/Sources/OneDSSwift) is compiled
// from source on top of the Obj-C module vended by the xcframework.
//
// Local development:
// 1. Run `tools/apple/build-xcframework.sh release` on macOS with Xcode.
// It produces ./build/apple/MATTelemetry.xcframework.
// 2. `swift build` validates macOS consumption; for iOS / Mac Catalyst /
// visionOS, add this package as a local dependency or build the package
// with the desired Xcode destination.
//
// Release distribution (so consumers can add the repo by URL in Xcode):
// .github/workflows/spm-release.yml builds and uploads the xcframework,
// computes the checksum, rewrites the local `.binaryTarget(... path:)` below
// to `url:`+`checksum:`, and pushes the 3-component SemVer tag that SPM can
// resolve.

import PackageDescription
import Foundation

let packageDirectory = URL(fileURLWithPath: #filePath).deletingLastPathComponent()

func readAvailability() -> [String: Bool] {
let candidates = [
"build/apple/MATTelemetryAvailability.json",
"tools/apple/MATTelemetryAvailability.json",
]

for relativePath in candidates {
let url = packageDirectory.appendingPathComponent(relativePath).standardizedFileURL
guard let data = try? Data(contentsOf: url),
let object = try? JSONSerialization.jsonObject(with: data) as? [String: Bool] else {
continue
}
return object
}

return [:]
}

let availability = readAvailability()
let hasDiagnosticDataViewer = availability["diagnosticDataViewer"] ?? false
let hasPrivacyGuard = availability["privacyGuard"] ?? false
let hasSanitizer = availability["sanitizer"] ?? false

var excludedSources: [String] = []
var swiftSettings: [SwiftSetting] = []

if !hasDiagnosticDataViewer {
excludedSources.append("DiagnosticDataViewer.swift")
}

if hasPrivacyGuard {
swiftSettings.append(.define("MATSDK_PRIVACYGUARD_AVAILABLE"))
} else {
excludedSources.append(contentsOf: [
"PrivacyGuard.swift",
"PrivacyGuardInitConfig.swift",
])
}

if !hasSanitizer {
excludedSources.append(contentsOf: [
"Sanitizer.swift",
"SanitizerInitConfig.swift",
])
}

let package = Package(
name: "OneDSSwift",
platforms: [
.iOS(.v12),
.macCatalyst(.v14),
.macOS(.v10_15),
.visionOS(.v1),
],
Comment thread
bmehta001 marked this conversation as resolved.
Comment thread
bmehta001 marked this conversation as resolved.
Comment thread
bmehta001 marked this conversation as resolved.
products: [
.library(name: "OneDSSwift", targets: ["OneDSSwift"]),
],
targets: [
// Prebuilt C++ core + Obj-C wrappers. The xcframework's bundled
// module map vends the Clang module `MATTelemetryObjC` (see
// tools/apple/module.modulemap), which the Swift layer imports.
//
// For a tagged release, swap the local path for the hosted artifact:
//
// .binaryTarget(
// name: "MATTelemetry",
// url: "https://github.com/microsoft/cpp_client_telemetry/releases/download/v3.10.161.1/MATTelemetry.xcframework.zip",
// checksum: "<output of swift package compute-checksum>"),
.binaryTarget(
name: "MATTelemetry",
path: "build/apple/MATTelemetry.xcframework"),

// Thin Swift API layer (source). Depends on the Obj-C module from the
// xcframework. The conditional source exclusions above must stay in sync
// with the headers baked into the xcframework.
.target(
name: "OneDSSwift",
dependencies: ["MATTelemetry"],
path: "wrappers/swift/Sources/OneDSSwift",
exclude: excludedSources,
swiftSettings: swiftSettings,
linkerSettings: [
.linkedLibrary("c++"),
.linkedLibrary("sqlite3"),
.linkedLibrary("z"),
.linkedFramework("CFNetwork", .when(platforms: [.iOS, .macCatalyst, .macOS, .visionOS])),
.linkedFramework("CoreFoundation", .when(platforms: [.iOS, .macCatalyst, .macOS, .visionOS])),
.linkedFramework("Foundation", .when(platforms: [.iOS, .macCatalyst, .macOS, .visionOS])),
.linkedFramework("Network", .when(platforms: [.iOS, .macCatalyst, .macOS, .visionOS])),
.linkedFramework("SystemConfiguration", .when(platforms: [.iOS, .macCatalyst, .macOS, .visionOS])),
.linkedFramework("IOKit", .when(platforms: [.macOS])),
.linkedFramework("UIKit", .when(platforms: [.iOS, .macCatalyst, .visionOS])),
]),
]
)
Loading
Loading