Skip to content
Merged
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
38 changes: 31 additions & 7 deletions .github/workflows/spm-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ name: SPM release (xcframework)
# * Uses the default GITHUB_TOKEN (needs contents: write). No extra secrets.

on:
pull_request:
branches: [main]
paths:
- Package.swift
- wrappers/swift/**
- tools/apple/**
- tests/apple/**
- .github/workflows/spm-release.yml
release:
types: [published]
workflow_dispatch:
Expand All @@ -32,16 +40,30 @@ permissions:
concurrency:
# Serialize all SPM publications because different vX.Y.Z.W releases can map
# to the same X.Y.Z tag and release asset.
group: spm-release
cancel-in-progress: false
group: spm-${{ github.event_name == 'pull_request' && github.event.pull_request.number || 'release' }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
package-consumption:
name: Validate released SwiftPM binary
if: ${{ github.event_name == 'pull_request' }}
runs-on: macos-15
permissions:
contents: read
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- name: Test hosted and local manifest selection
run: python3 tests/apple/test-spm-package.py
- name: Build Swift wrapper against the official release
run: swift build

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) }}
(github.event_name == 'release' &&
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
Expand Down Expand Up @@ -115,6 +137,8 @@ jobs:

- name: Validate local SwiftPM package consumption
if: ${{ steps.ver.outputs.skip != 'true' && steps.pub.outputs.published != 'true' }}
env:
MATSDK_SPM_USE_LOCAL_XCFRAMEWORK: "1"
run: |
set -euo pipefail
swift package dump-package > package.json
Expand Down Expand Up @@ -171,10 +195,10 @@ jobs:
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"
out, count = re.subn(
r'\.binaryTarget\(\s*name:\s*"MATTelemetry",\s*url:\s*"[^"]*",\s*checksum:\s*"[^"]*"\s*\)',
repl, src)
assert count == 1, "Expected exactly one hosted MATTelemetry binary target"
open(path, "w").write(out)
PY

Expand Down
35 changes: 17 additions & 18 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,28 @@
// Swift Package Manager manifest for the 1DS C++ SDK (Microsoft Applications
// Telemetry) on Apple platforms.
//
// PROTOTYPE — distribution model:
// 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:
// Uses the official release xcframework by default; no local C++ build is needed.
//
// Local development (opt-in):
// 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 /
// 2. `MATSDK_SPM_USE_LOCAL_XCFRAMEWORK=1 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, validates, and uploads the
// xcframework, rewrites the local `.binaryTarget(... path:)` below to
// `url:`+`checksum:`, removes repository submodules from the tag-only commit,
// xcframework, updates the hosted binary target's URL and checksum below,
// removes repository submodules from the tag-only commit,
// validates that commit through a clean consumer, and pushes the
// 3-component SemVer tag that SPM can resolve. Because vX.Y.Z.W release tags
// map to one X.Y.Z SPM tag, only one build per three-component version can
Expand All @@ -35,12 +38,12 @@ import PackageDescription
import Foundation

let packageDirectory = URL(fileURLWithPath: #filePath).deletingLastPathComponent()
let useLocalXCFramework = ProcessInfo.processInfo.environment["MATSDK_SPM_USE_LOCAL_XCFRAMEWORK"] == "1"

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

for relativePath in candidates {
let url = packageDirectory.appendingPathComponent(relativePath).standardizedFileURL
Expand Down Expand Up @@ -97,16 +100,12 @@ let package = Package(
// 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(
useLocalXCFramework ? .binaryTarget(
name: "MATTelemetry",
path: "build/apple/MATTelemetry.xcframework") : .binaryTarget(
name: "MATTelemetry",
path: "build/apple/MATTelemetry.xcframework"),
url: "https://github.com/microsoft/cpp_client_telemetry/releases/download/v3.10.281.1/MATTelemetry.xcframework.zip",
checksum: "799b956dde6c6dccdc46b762b14eca55f677608f81b12879031fbd9c91470812"),

// Thin Swift API layer (source). Depends on the Obj-C module from the
// xcframework. The conditional source exclusions above must stay in sync
Expand Down
6 changes: 3 additions & 3 deletions lib/include/public/Version.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
#define MAT_VERSION_HPP
// WARNING: DO NOT MODIFY THIS FILE!
// This file has been automatically generated, manual changes will be lost.
#define BUILD_VERSION_STR "3.10.281.1"
#define BUILD_VERSION 3,10,281,1
#define BUILD_VERSION_STR "3.10.309.1"
#define BUILD_VERSION 3,10,309,1

#ifndef RESOURCE_COMPILER_INVOKED
#include "ctmacros.hpp"
Expand All @@ -18,7 +18,7 @@ namespace MAT_NS_BEGIN {
uint64_t const Version =
((uint64_t)3 << 48) |
((uint64_t)10 << 32) |
((uint64_t)281 << 16) |
((uint64_t)309 << 16) |
((uint64_t)1);

} MAT_NS_END
Expand Down
75 changes: 75 additions & 0 deletions tests/apple/test-spm-package.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
"""Exercise SwiftPM binary selection without building the C++ SDK."""

import json
import os
from pathlib import Path
import shutil
import subprocess
import tempfile
import unittest


REPO_ROOT = Path(__file__).resolve().parents[2]


class SPMPackageTests(unittest.TestCase):
def setUp(self):
self.temporary = tempfile.TemporaryDirectory()
self.addCleanup(self.temporary.cleanup)
self.root = Path(self.temporary.name)
shutil.copy2(REPO_ROOT / "Package.swift", self.root)
metadata = self.root / "tools" / "apple"
metadata.mkdir(parents=True)
shutil.copy2(
REPO_ROOT / "tools" / "apple" / "MATTelemetryAvailability.json", metadata
)
self.local = self.root / "build" / "apple"
self.local.mkdir(parents=True)
(self.local / "MATTelemetryAvailability.json").write_text(
json.dumps({
"diagnosticDataViewer": True,
"privacyGuard": True,
"sanitizer": True,
}),
encoding="utf-8",
)

def manifest(self, use_local=False):
environment = dict(os.environ)
environment.pop("MATSDK_SPM_USE_LOCAL_XCFRAMEWORK", None)
if use_local:
environment["MATSDK_SPM_USE_LOCAL_XCFRAMEWORK"] = "1"
result = subprocess.run(
["swift", "package", "--package-path", str(self.root), "dump-package"],
env=environment,
check=True,
capture_output=True,
text=True,
)
return {target["name"]: target for target in json.loads(result.stdout)["targets"]}

def test_default_uses_release_without_local_framework_or_local_modules(self):
targets = self.manifest()
binary = targets["MATTelemetry"]
self.assertRegex(
binary["url"],
r"^https://github\.com/microsoft/cpp_client_telemetry/releases/download/"
r"v\d+\.\d+\.\d+\.\d+/MATTelemetry\.xcframework\.zip$",
)
self.assertRegex(binary["checksum"], r"^[0-9a-f]{64}$")
self.assertIsNone(binary.get("path"))
self.assertIn("DiagnosticDataViewer.swift", targets["OneDSSwift"]["exclude"])
self.assertIn("PrivacyGuard.swift", targets["OneDSSwift"]["exclude"])
self.assertIn("Sanitizer.swift", targets["OneDSSwift"]["exclude"])

def test_local_opt_in_uses_matching_framework_and_availability(self):
targets = self.manifest(use_local=True)
binary = targets["MATTelemetry"]
self.assertEqual(binary["path"], "build/apple/MATTelemetry.xcframework")
self.assertIsNone(binary.get("url"))
self.assertIsNone(binary.get("checksum"))
self.assertEqual(targets["OneDSSwift"]["exclude"], [])


if __name__ == "__main__":
unittest.main()
15 changes: 9 additions & 6 deletions tools/apple/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,15 +73,18 @@ tools/apple/build-xcframework.sh release
# -> prints the SwiftPM checksum
```

For local development, `Package.swift` points at
`build/apple/MATTelemetry.xcframework`. `swift build` validates macOS
By default, `Package.swift` downloads the official release xcframework with its
pinned checksum; no local C++ build is needed. To use
`build/apple/MATTelemetry.xcframework` for local development, run
`MATSDK_SPM_USE_LOCAL_XCFRAMEWORK=1 swift build`. This validates macOS
consumption; use Xcode destinations for iOS Simulator, Mac Catalyst, and
visionOS Simulator.
visionOS Simulator with the same environment variable. Local availability
metadata is used only in this opt-in mode.

## Consumption

- **Local:** add this repository as a local package dependency after building
`build/apple/MATTelemetry.xcframework`.
- **Checkout:** run `swift build` or add this repository as a local package
dependency to use the official prebuilt binary without building the C++ SDK.
- **Released:** add the repository URL in Xcode and pin the parallel
3-component SemVer tag published by the release workflow:

Expand All @@ -101,7 +104,7 @@ dispatch. It:
2. Validates package consumption for macOS, iOS Simulator, Mac Catalyst, and
visionOS Simulator.
3. Uploads the zip to the GitHub Release.
4. Rewrites `Package.swift` from local `path:` to hosted `url:` + `checksum:`.
4. Updates the hosted `url:` + `checksum:` in `Package.swift` for the new release.
5. Creates a self-contained tag commit without the repository's submodules.
6. Resolves and builds that tag from a clean external Swift package.
7. Pushes the 3-component SPM tag.
Expand Down
Loading