Skip to content

Commit 91701a2

Browse files
pblazejclaude
andcommitted
fix: various xcframework script improvements
- Fix dylib install name to use @rpath (fixes runtime loading) - Add SwiftProtobuf as dependency in output Package.swift - Replace _LiveKitStub with LiveKitTargets wrapper target - Copy protobuf dependency line verbatim from Package.swift - Exclude x86_64 for tvOS/visionOS simulators (missing in WebRTC) - Always create zip (including --local mode) - Use .build/ as default output dir, revert .gitignore - Throw ValidationError instead of fatalError for missing deps Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent e90f14c commit 91701a2

3 files changed

Lines changed: 44 additions & 30 deletions

File tree

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
.DS_Store
22
.build
33
.claude
4-
build
54
/Packages
65
/*.xcodeproj
76
.swiftpm/

scripts/Package.swift.stencil

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,22 @@ let package = Package(
1313
.visionOS(.v26),
1414
],
1515
products: [
16-
.library(name: "LiveKit", targets: ["LiveKit", "LiveKitWebRTC", "RustLiveKitUniFFI", "_LiveKitStub"]),
16+
.library(name: "LiveKit", targets: ["LiveKitTargets"]),
1717
],
18-
targets: [{% if local %}
19-
.binaryTarget(name: "LiveKit", path: "LiveKit.xcframework"),{% else %}
18+
dependencies: [
19+
{{ protobufDependency }}
20+
],
21+
targets: [
22+
.target(
23+
name: "LiveKitTargets",
24+
dependencies: [
25+
"LiveKit",
26+
"LiveKitWebRTC",
27+
"RustLiveKitUniFFI",
28+
.product(name: "SwiftProtobuf", package: "swift-protobuf"),
29+
]
30+
),{% if local %}
31+
.binaryTarget(name: "LiveKit", path: "LiveKit.xcframework.zip"),{% else %}
2032
.binaryTarget(
2133
name: "LiveKit",
2234
url: "{{ baseURL }}/LiveKit.xcframework.zip",
@@ -32,9 +44,5 @@ let package = Package(
3244
url: "{{ uniffiURL }}",
3345
checksum: "{{ uniffiChecksum }}"
3446
),
35-
// Without at least one regular (non-binary) target, this package doesn't show up
36-
// in Xcode under "Frameworks, Libraries, and Embedded Content".
37-
// https://github.com/apple/swift-package-manager/issues/6069
38-
.target(name: "_LiveKitStub", path: "Sources/_LiveKitStub"),
3947
]
4048
)

scripts/xcframework.swift

Lines changed: 29 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ func exec(_ args: [String]) async throws -> String {
6060

6161
struct Platform {
6262
let label, destination, archiveName: String
63+
var extraSettings: [String] = []
6364
}
6465

6566
let platforms: [Platform] = [
@@ -68,9 +69,9 @@ let platforms: [Platform] = [
6869
.init(label: "macOS", destination: "generic/platform=macOS", archiveName: "macos-arm64_x86_64"),
6970
.init(label: "Mac Catalyst", destination: "generic/platform=macOS,variant=Mac Catalyst", archiveName: "ios-arm64_x86_64-maccatalyst"),
7071
.init(label: "tvOS Device", destination: "generic/platform=tvOS", archiveName: "tvos-arm64"),
71-
.init(label: "tvOS Simulator", destination: "generic/platform=tvOS Simulator", archiveName: "tvos-arm64_x86_64-simulator"),
72+
.init(label: "tvOS Simulator", destination: "generic/platform=tvOS Simulator", archiveName: "tvos-arm64_x86_64-simulator", extraSettings: ["ARCHS=arm64"]),
7273
.init(label: "visionOS Device", destination: "generic/platform=visionOS", archiveName: "xros-arm64"),
73-
.init(label: "visionOS Simulator", destination: "generic/platform=visionOS Simulator", archiveName: "xros-arm64-simulator"),
74+
.init(label: "visionOS Simulator", destination: "generic/platform=visionOS Simulator", archiveName: "xros-arm64-simulator", extraSettings: ["ARCHS=arm64"]),
7475
]
7576

7677
// MARK: - Binary dependency info
@@ -113,6 +114,13 @@ func parseBinaryDep(repoRoot: Path, spmDir: Path, pattern: String, xcfw: String)
113114
return BinaryDep(url: zipURL, checksum: String(csMatch.hash))
114115
}
115116

117+
func protobufDependencyLine(repoRoot: Path) throws -> String {
118+
let contents: String = try (repoRoot + "Package.swift").read()
119+
guard let line = contents.components(separatedBy: .newlines).first(where: { $0.contains("swift-protobuf") && $0.contains(".package") })
120+
else { throw ValidationError("swift-protobuf dependency not found in Package.swift") }
121+
return line
122+
}
123+
116124
// MARK: - Xcode project generation
117125

118126
/// Recursively add source files from a directory to an Xcode group and build phase.
@@ -213,17 +221,17 @@ func generateFrameworkProject(at projectPath: Path, repoRoot: Path) throws {
213221

214222
// Parse versions from Package.swift
215223
let pkgContents: String = try (repoRoot + "Package.swift").read()
216-
func extractVersion(pattern: String) -> String {
224+
func extractVersion(pattern: String) throws -> String {
217225
guard let line = pkgContents.components(separatedBy: .newlines)
218226
.first(where: { $0.contains(pattern) }),
219227
let match = line.firstMatch(of: #/exact:\s*"(?<ver>[^"]+)"/#) ?? line.firstMatch(of: #/from:\s*"(?<ver>[^"]+)"/#)
220-
else { return "1.0.0" }
228+
else { throw ValidationError("\(pattern) version not found in Package.swift") }
221229
return String(match.ver)
222230
}
223231

224-
let webrtcPkg = addRemotePackage(url: "https://github.com/livekit/webrtc-xcframework.git", version: extractVersion(pattern: "webrtc-xcframework"))
225-
let uniffiPkg = addRemotePackage(url: "https://github.com/livekit/livekit-uniffi-xcframework.git", version: extractVersion(pattern: "uniffi-xcframework"))
226-
let protobufPkg = addRemotePackage(url: "https://github.com/apple/swift-protobuf.git", version: extractVersion(pattern: "swift-protobuf"))
232+
let webrtcPkg = try addRemotePackage(url: "https://github.com/livekit/webrtc-xcframework.git", version: extractVersion(pattern: "webrtc-xcframework"))
233+
let uniffiPkg = try addRemotePackage(url: "https://github.com/livekit/livekit-uniffi-xcframework.git", version: extractVersion(pattern: "uniffi-xcframework"))
234+
let protobufPkg = try addRemotePackage(url: "https://github.com/apple/swift-protobuf.git", version: extractVersion(pattern: "swift-protobuf"))
227235

228236
let webrtcDep = addProductDep(name: "LiveKitWebRTC", package: webrtcPkg)
229237
let uniffiDep = addProductDep(name: "LiveKitUniFFI", package: uniffiPkg)
@@ -249,6 +257,7 @@ func generateFrameworkProject(at projectPath: Path, repoRoot: Path) throws {
249257
"SKIP_INSTALL": "NO",
250258
"BUILD_LIBRARY_FOR_DISTRIBUTION": "YES",
251259
"INSTALL_PATH": "$(LOCAL_LIBRARY_DIR)/Frameworks",
260+
"DYLIB_INSTALL_NAME_BASE": "@rpath",
252261
"DEFINES_MODULE": "YES",
253262
"MACH_O_TYPE": "mh_dylib",
254263
"CLANG_ENABLE_MODULES": "YES",
@@ -352,7 +361,7 @@ struct BuildXCFramework: AsyncParsableCommand {
352361
repoRoot = repoRoot.parent()
353362
}
354363

355-
let outputDir = Path(output ?? (repoRoot + "build" + "xcframework").string)
364+
let outputDir = Path(output ?? (repoRoot + ".build" + "xcframework").string)
356365
let buildDir = Path(NSTemporaryDirectory()) + "livekit-xcfw-\(UUID().uuidString)"
357366
let spmDir = buildDir + "spm"
358367

@@ -402,7 +411,7 @@ struct BuildXCFramework: AsyncParsableCommand {
402411
"-archivePath", archive.string,
403412
"-derivedDataPath", dd.string,
404413
"-clonedSourcePackagesDirPath", spmDir.string,
405-
])
414+
] + p.extraSettings)
406415
let lastLine = archiveOutput.components(separatedBy: .newlines).last ?? ""
407416
print("\(p.label): \(lastLine)")
408417
return (p, .success(archive))
@@ -443,15 +452,12 @@ struct BuildXCFramework: AsyncParsableCommand {
443452
let xcfwOutput = try await exec(xcfwArgs)
444453
if !xcfwOutput.isEmpty { print(xcfwOutput) }
445454

446-
// --- Zip & checksum (release builds only) ---
447-
var liveKitChecksum = ""
448-
if !local {
449-
step("Zipping LiveKit.xcframework...")
450-
let zipPath = outputDir + "LiveKit.xcframework.zip"
451-
try fm.zipItem(at: xcfwPath.url, to: zipPath.url)
452-
liveKitChecksum = try await exec("swift", "package", "compute-checksum", zipPath.string)
453-
print(" LiveKit.xcframework.zip checksum: \(liveKitChecksum)")
454-
}
455+
// --- Zip & checksum ---
456+
step("Zipping LiveKit.xcframework...")
457+
let zipPath = outputDir + "LiveKit.xcframework.zip"
458+
try fm.zipItem(at: xcfwPath.url, to: zipPath.url)
459+
let liveKitChecksum = try await exec("swift", "package", "compute-checksum", zipPath.string)
460+
print(" LiveKit.xcframework.zip checksum: \(liveKitChecksum)")
455461

456462
// --- Generate Package.swift ---
457463
step("Generating Package.swift...")
@@ -494,12 +500,13 @@ struct BuildXCFramework: AsyncParsableCommand {
494500
"webrtcChecksum": webrtcDep.checksum,
495501
"uniffiURL": uniffiDep.url,
496502
"uniffiChecksum": uniffiDep.checksum,
503+
"protobufDependency": protobufDependencyLine(repoRoot: repoRoot),
497504
])
498505
try (outputDir + "Package.swift").write(rendered)
499506

500-
// Create stub target (workaround for apple/swift-package-manager#6069)
501-
let stubDir = outputDir + "Sources" + "_LiveKitStub"
502-
try stubDir.mkpath()
503-
try (stubDir + "Stub.swift").write("// Workaround: without a non-binary target, SPM won't show the\n// package in Xcode's \"Frameworks, Libraries, and Embedded Content\".\n// See https://github.com/apple/swift-package-manager/issues/6069\nclass _LiveKitStub {}\n")
507+
// LiveKitTargets needs at least one source file
508+
let targetsDir = outputDir + "Sources" + "LiveKitTargets"
509+
try targetsDir.mkpath()
510+
try (targetsDir + "LiveKitTargets.swift").write("// Re-export LiveKit so consumers only need `import LiveKit`.\n@_exported import LiveKit\n")
504511
}
505512
}

0 commit comments

Comments
 (0)