Skip to content

Commit d1f13c9

Browse files
committed
Attempt to use SDKSettings.json on Darwin platforms to set -target-sdk-version
1 parent aabc801 commit d1f13c9

File tree

6 files changed

+291
-0
lines changed

6 files changed

+291
-0
lines changed

Sources/SwiftDriver/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ add_library(SwiftDriver
7575
Utilities/Triple+Platforms.swift
7676
Utilities/Triple.swift
7777
Utilities/TypedVirtualPath.swift
78+
Utilities/VersionExtensions.swift
7879
Utilities/VirtualPath.swift)
7980

8081
target_link_libraries(SwiftDriver PUBLIC

Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,10 @@ extension Driver {
230230
if compilerMode != .repl {
231231
commandLine.appendFlags("-module-name", moduleOutputInfo.name)
232232
}
233+
234+
try toolchain.addPlatformSpecificCommonFrontendOptions(commandLine: &commandLine,
235+
inputs: &inputs,
236+
frontendTargetInfo: frontendTargetInfo)
233237
}
234238

235239
mutating func addFrontendSupplementaryOutputArguments(commandLine: inout [Job.ArgTemplate], primaryInputs: [TypedVirtualPath]) throws -> [TypedVirtualPath] {

Sources/SwiftDriver/Toolchains/DarwinToolchain.swift

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212
import TSCBasic
13+
import TSCUtility
14+
import Foundation
1315
import SwiftOptions
1416

1517
/// Toolchain for Darwin-based platforms, such as macOS and iOS.
@@ -205,6 +207,100 @@ public final class DarwinToolchain: Toolchain {
205207
}
206208
}
207209
}
210+
211+
private struct DarwinSDKInfo: Decodable {
212+
enum CodingKeys: String, CodingKey {
213+
case version = "Version"
214+
case versionMap = "VersionMap"
215+
}
216+
217+
struct VersionMap: Decodable {
218+
enum CodingKeys: String, CodingKey {
219+
case macOSToCatalystMapping = "macOS_iOSMac"
220+
}
221+
222+
var macOSToCatalystMapping: [Version: Version]
223+
224+
init(from decoder: Decoder) throws {
225+
let keyedContainer = try decoder.container(keyedBy: CodingKeys.self)
226+
227+
let mappingDict = try keyedContainer.decode([String: String].self, forKey: .macOSToCatalystMapping)
228+
self.macOSToCatalystMapping = [:]
229+
try mappingDict.forEach { key, value in
230+
guard let newKey = Version(potentiallyIncompleteVersionString: key) else {
231+
throw DecodingError.dataCorruptedError(forKey: .macOSToCatalystMapping,
232+
in: keyedContainer,
233+
debugDescription: "Malformed version string")
234+
}
235+
guard let newValue = Version(potentiallyIncompleteVersionString: value) else {
236+
throw DecodingError.dataCorruptedError(forKey: .macOSToCatalystMapping,
237+
in: keyedContainer,
238+
debugDescription: "Malformed version string")
239+
}
240+
self.macOSToCatalystMapping[newKey] = newValue
241+
}
242+
}
243+
}
244+
245+
var version: Version
246+
var versionMap: VersionMap
247+
248+
init(from decoder: Decoder) throws {
249+
let keyedContainer = try decoder.container(keyedBy: CodingKeys.self)
250+
251+
let versionString = try keyedContainer.decode(String.self, forKey: .version)
252+
guard let version = Version(potentiallyIncompleteVersionString: versionString) else {
253+
throw DecodingError.dataCorruptedError(forKey: .version,
254+
in: keyedContainer,
255+
debugDescription: "Malformed version string")
256+
}
257+
self.version = version
258+
self.versionMap = try keyedContainer.decode(VersionMap.self, forKey: .versionMap)
259+
}
260+
261+
func sdkVersion(for triple: Triple) -> Version {
262+
if triple.isMacCatalyst {
263+
// For the Mac Catalyst environment, we have a macOS SDK with a macOS
264+
// SDK version. Map that to the corresponding iOS version number to pass
265+
// down to the linker.
266+
return versionMap.macOSToCatalystMapping[version.withoutBuildNumbers] ?? Version(0, 0, 0)
267+
}
268+
return version
269+
}
270+
}
271+
272+
// SDK info is computed lazily. This should not generally be accessed directly.
273+
private var _sdkInfo: DarwinSDKInfo? = nil
274+
275+
private func getTargetSDKInfo(sdkPath: VirtualPath) -> DarwinSDKInfo? {
276+
if let info = _sdkInfo {
277+
return info
278+
} else {
279+
let sdkSettingsPath = sdkPath.appending(component: "SDKSettings.json")
280+
guard let contents = try? fileSystem.readFileContents(sdkSettingsPath) else { return nil }
281+
guard let sdkInfo = try? JSONDecoder().decode(DarwinSDKInfo.self,
282+
from: Data(contents.contents)) else { return nil }
283+
self._sdkInfo = sdkInfo
284+
return sdkInfo
285+
}
286+
}
287+
288+
public func addPlatformSpecificCommonFrontendOptions(
289+
commandLine: inout [Job.ArgTemplate],
290+
inputs: inout [TypedVirtualPath],
291+
frontendTargetInfo: FrontendTargetInfo
292+
) throws {
293+
guard let sdkPath = try frontendTargetInfo.paths.sdkPath.map(VirtualPath.init(path:)),
294+
let sdkInfo = getTargetSDKInfo(sdkPath: sdkPath) else { return }
295+
296+
commandLine.append(.flag("-target-sdk-version"))
297+
commandLine.append(.flag(sdkInfo.sdkVersion(for: frontendTargetInfo.target.triple).description))
298+
299+
if let targetVariantTriple = frontendTargetInfo.targetVariant?.triple {
300+
commandLine.append(.flag("-target-variant-sdk-version"))
301+
commandLine.append(.flag(sdkInfo.sdkVersion(for: targetVariantTriple).description))
302+
}
303+
}
208304
}
209305

210306
extension Diagnostic.Message {

Sources/SwiftDriver/Toolchains/Toolchain.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,12 @@ public protocol Toolchain {
8181
parsedOptions: inout ParsedOptions,
8282
sdkPath: String?,
8383
targetTriple: Triple) throws -> [String: String]
84+
85+
func addPlatformSpecificCommonFrontendOptions(
86+
commandLine: inout [Job.ArgTemplate],
87+
inputs: inout [TypedVirtualPath],
88+
frontendTargetInfo: FrontendTargetInfo
89+
) throws
8490
}
8591

8692
extension Toolchain {
@@ -156,6 +162,12 @@ extension Toolchain {
156162
public func validateArgs(_ parsedOptions: inout ParsedOptions,
157163
targetTriple: Triple,
158164
targetVariantTriple: Triple?, diagnosticsEngine: DiagnosticsEngine) {}
165+
166+
public func addPlatformSpecificCommonFrontendOptions(
167+
commandLine: inout [Job.ArgTemplate],
168+
inputs: inout [TypedVirtualPath],
169+
frontendTargetInfo: FrontendTargetInfo
170+
) throws {}
159171
}
160172

161173
public enum ToolchainError: Swift.Error {
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
//===---------- VersionExtensions.swift - Version Parsing Utilities -------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2020 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import TSCUtility
14+
15+
// TODO: maybe move this to TSC.
16+
extension Version {
17+
/// Create a version from a string, replacing unknown trailing components with '0'.
18+
init?(potentiallyIncompleteVersionString string: String) {
19+
// This is a copied version of TSC's version parsing, modified to fill
20+
// in missing components if needed.
21+
let prereleaseStartIndex = string.firstIndex(of: "-")
22+
let metadataStartIndex = string.firstIndex(of: "+")
23+
24+
let requiredEndIndex = prereleaseStartIndex ?? metadataStartIndex ?? string.endIndex
25+
let requiredCharacters = string.prefix(upTo: requiredEndIndex)
26+
var requiredComponents = requiredCharacters
27+
.split(separator: ".", maxSplits: 2, omittingEmptySubsequences: false)
28+
.map(String.init).compactMap({ Int($0) }).filter({ $0 >= 0 })
29+
30+
requiredComponents.append(contentsOf:
31+
Array(repeating: 0,
32+
count: max(0, 3 - requiredComponents.count)))
33+
34+
let major = requiredComponents[0]
35+
let minor = requiredComponents[1]
36+
let patch = requiredComponents[2]
37+
38+
func identifiers(start: String.Index?, end: String.Index) -> [String] {
39+
guard let start = start else { return [] }
40+
let identifiers = string[string.index(after: start)..<end]
41+
return identifiers.split(separator: ".").map(String.init)
42+
}
43+
44+
let prereleaseIdentifiers = identifiers(
45+
start: prereleaseStartIndex,
46+
end: metadataStartIndex ?? string.endIndex)
47+
let buildMetadataIdentifiers = identifiers(
48+
start: metadataStartIndex,
49+
end: string.endIndex)
50+
51+
self.init(major, minor, patch,
52+
prereleaseIdentifiers: prereleaseIdentifiers,
53+
buildMetadataIdentifiers: buildMetadataIdentifiers)
54+
}
55+
56+
/// Returns the version with out any build/release metadata numbers.
57+
var withoutBuildNumbers: Version {
58+
return Version(self.major, self.minor, self.patch)
59+
}
60+
}

Tests/SwiftDriverTests/SwiftDriverTests.swift

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1608,6 +1608,124 @@ final class SwiftDriverTests: XCTestCase {
16081608
try assertNoDriverDiagnostics(args: "swiftc", "-c", "-target", "x86_64-apple-macosx10.14", "-link-objc-runtime", "foo.swift")
16091609
}
16101610

1611+
// Test cases ported from Driver/macabi-environment.swift
1612+
func testDarwinSDKVersioning() throws {
1613+
try withTemporaryDirectory { tmpDir in
1614+
let sdk1 = tmpDir.appending(component: "MacOSX10.15.versioned.sdk")
1615+
try localFileSystem.writeFileContents(sdk1.appending(component: "SDKSettings.json")) {
1616+
$0 <<< """
1617+
{
1618+
"Version":"10.15",
1619+
"VersionMap" : {
1620+
"macOS_iOSMac" : {
1621+
"10.15" : "13.1",
1622+
"10.15.1" : "13.2"
1623+
},
1624+
"iOSMac_macOS" : {
1625+
"13.1" : "10.15",
1626+
"13.2" : "10.15.1"
1627+
}
1628+
}
1629+
}
1630+
"""
1631+
}
1632+
1633+
let sdk2 = tmpDir.appending(component: "MacOSX10.15.4.versioned.sdk")
1634+
try localFileSystem.writeFileContents(sdk2.appending(component: "SDKSettings.json")) {
1635+
$0 <<< """
1636+
{
1637+
"Version":"10.15.4",
1638+
"VersionMap" : {
1639+
"macOS_iOSMac" : {
1640+
"10.14.4" : "12.4",
1641+
"10.14.3" : "12.3",
1642+
"10.14.2" : "12.2",
1643+
"10.14.1" : "12.1",
1644+
"10.15" : "13.0",
1645+
"10.14" : "12.0",
1646+
"10.14.5" : "12.5",
1647+
"10.15.1" : "13.2",
1648+
"10.15.4" : "13.4"
1649+
},
1650+
"iOSMac_macOS" : {
1651+
"13.0" : "10.15",
1652+
"12.3" : "10.14.3",
1653+
"12.0" : "10.14",
1654+
"12.4" : "10.14.4",
1655+
"12.1" : "10.14.1",
1656+
"12.5" : "10.14.5",
1657+
"12.2" : "10.14.2",
1658+
"13.2" : "10.15.1",
1659+
"13.4" : "10.15.4"
1660+
}
1661+
}
1662+
}
1663+
"""
1664+
}
1665+
1666+
do {
1667+
var driver = try Driver(args: ["swiftc",
1668+
"-target", "x86_64-apple-macosx10.14",
1669+
"-sdk", sdk1.description,
1670+
"foo.swift"])
1671+
let frontendJobs = try driver.planBuild()
1672+
XCTAssertEqual(frontendJobs[0].kind, .compile)
1673+
XCTAssertTrue(frontendJobs[0].commandLine.contains(subsequence: [
1674+
.flag("-target-sdk-version"),
1675+
.flag("10.15.0")
1676+
]))
1677+
}
1678+
1679+
do {
1680+
var driver = try Driver(args: ["swiftc",
1681+
"-target", "x86_64-apple-macosx10.14",
1682+
"-target-variant", "x86_64-apple-ios13.0-macabi",
1683+
"-sdk", sdk1.description,
1684+
"foo.swift"])
1685+
let frontendJobs = try driver.planBuild()
1686+
XCTAssertEqual(frontendJobs[0].kind, .compile)
1687+
XCTAssertTrue(frontendJobs[0].commandLine.contains(subsequence: [
1688+
.flag("-target-sdk-version"),
1689+
.flag("10.15.0"),
1690+
.flag("-target-variant-sdk-version"),
1691+
.flag("13.1.0")
1692+
]))
1693+
}
1694+
1695+
do {
1696+
var driver = try Driver(args: ["swiftc",
1697+
"-target", "x86_64-apple-macosx10.14",
1698+
"-target-variant", "x86_64-apple-ios13.0-macabi",
1699+
"-sdk", sdk2.description,
1700+
"foo.swift"])
1701+
let frontendJobs = try driver.planBuild()
1702+
XCTAssertEqual(frontendJobs[0].kind, .compile)
1703+
XCTAssertTrue(frontendJobs[0].commandLine.contains(subsequence: [
1704+
.flag("-target-sdk-version"),
1705+
.flag("10.15.4"),
1706+
.flag("-target-variant-sdk-version"),
1707+
.flag("13.4.0")
1708+
]))
1709+
}
1710+
1711+
do {
1712+
var driver = try Driver(args: ["swiftc",
1713+
"-target-variant", "x86_64-apple-macosx10.14",
1714+
"-target", "x86_64-apple-ios13.0-macabi",
1715+
"-sdk", sdk2.description,
1716+
"foo.swift"])
1717+
let frontendJobs = try driver.planBuild()
1718+
XCTAssertEqual(frontendJobs[0].kind, .compile)
1719+
XCTAssertTrue(frontendJobs[0].commandLine.contains(subsequence: [
1720+
.flag("-target-sdk-version"),
1721+
.flag("13.4.0"),
1722+
.flag("-target-variant-sdk-version"),
1723+
.flag("10.15.4")
1724+
]))
1725+
}
1726+
}
1727+
}
1728+
16111729
func testDSYMGeneration() throws {
16121730
let commonArgs = [
16131731
"swiftc", "foo.swift", "bar.swift",

0 commit comments

Comments
 (0)