Skip to content

Attempt to use SDKSettings.json on Darwin platforms to set -target-sdk-version #209

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 18, 2020
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
1 change: 1 addition & 0 deletions Sources/SwiftDriver/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ add_library(SwiftDriver
Utilities/Triple+Platforms.swift
Utilities/Triple.swift
Utilities/TypedVirtualPath.swift
Utilities/VersionExtensions.swift
Utilities/VirtualPath.swift)

target_link_libraries(SwiftDriver PUBLIC
Expand Down
4 changes: 4 additions & 0 deletions Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,10 @@ extension Driver {
if compilerMode != .repl {
commandLine.appendFlags("-module-name", moduleOutputInfo.name)
}

try toolchain.addPlatformSpecificCommonFrontendOptions(commandLine: &commandLine,
inputs: &inputs,
frontendTargetInfo: frontendTargetInfo)
}

mutating func addFrontendSupplementaryOutputArguments(commandLine: inout [Job.ArgTemplate], primaryInputs: [TypedVirtualPath]) throws -> [TypedVirtualPath] {
Expand Down
96 changes: 96 additions & 0 deletions Sources/SwiftDriver/Toolchains/DarwinToolchain.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
//
//===----------------------------------------------------------------------===//
import TSCBasic
import TSCUtility
import Foundation
import SwiftOptions

/// Toolchain for Darwin-based platforms, such as macOS and iOS.
Expand Down Expand Up @@ -205,6 +207,100 @@ public final class DarwinToolchain: Toolchain {
}
}
}

private struct DarwinSDKInfo: Decodable {
enum CodingKeys: String, CodingKey {
case version = "Version"
case versionMap = "VersionMap"
}

struct VersionMap: Decodable {
enum CodingKeys: String, CodingKey {
case macOSToCatalystMapping = "macOS_iOSMac"
}

var macOSToCatalystMapping: [Version: Version]

init(from decoder: Decoder) throws {
let keyedContainer = try decoder.container(keyedBy: CodingKeys.self)

let mappingDict = try keyedContainer.decode([String: String].self, forKey: .macOSToCatalystMapping)
self.macOSToCatalystMapping = [:]
try mappingDict.forEach { key, value in
guard let newKey = Version(potentiallyIncompleteVersionString: key) else {
throw DecodingError.dataCorruptedError(forKey: .macOSToCatalystMapping,
in: keyedContainer,
debugDescription: "Malformed version string")
}
guard let newValue = Version(potentiallyIncompleteVersionString: value) else {
throw DecodingError.dataCorruptedError(forKey: .macOSToCatalystMapping,
in: keyedContainer,
debugDescription: "Malformed version string")
}
self.macOSToCatalystMapping[newKey] = newValue
}
}
}

var version: Version
var versionMap: VersionMap

init(from decoder: Decoder) throws {
let keyedContainer = try decoder.container(keyedBy: CodingKeys.self)

let versionString = try keyedContainer.decode(String.self, forKey: .version)
guard let version = Version(potentiallyIncompleteVersionString: versionString) else {
throw DecodingError.dataCorruptedError(forKey: .version,
in: keyedContainer,
debugDescription: "Malformed version string")
}
self.version = version
self.versionMap = try keyedContainer.decode(VersionMap.self, forKey: .versionMap)
}

func sdkVersion(for triple: Triple) -> Version {
if triple.isMacCatalyst {
// For the Mac Catalyst environment, we have a macOS SDK with a macOS
// SDK version. Map that to the corresponding iOS version number to pass
// down to the linker.
return versionMap.macOSToCatalystMapping[version.withoutBuildNumbers] ?? Version(0, 0, 0)
}
return version
}
}

// SDK info is computed lazily. This should not generally be accessed directly.
private var _sdkInfo: DarwinSDKInfo? = nil

private func getTargetSDKInfo(sdkPath: VirtualPath) -> DarwinSDKInfo? {
if let info = _sdkInfo {
return info
} else {
let sdkSettingsPath = sdkPath.appending(component: "SDKSettings.json")
guard let contents = try? fileSystem.readFileContents(sdkSettingsPath) else { return nil }
guard let sdkInfo = try? JSONDecoder().decode(DarwinSDKInfo.self,
from: Data(contents.contents)) else { return nil }
self._sdkInfo = sdkInfo
return sdkInfo
}
}

public func addPlatformSpecificCommonFrontendOptions(
commandLine: inout [Job.ArgTemplate],
inputs: inout [TypedVirtualPath],
frontendTargetInfo: FrontendTargetInfo
) throws {
guard let sdkPath = try frontendTargetInfo.paths.sdkPath.map(VirtualPath.init(path:)),
let sdkInfo = getTargetSDKInfo(sdkPath: sdkPath) else { return }

commandLine.append(.flag("-target-sdk-version"))
commandLine.append(.flag(sdkInfo.sdkVersion(for: frontendTargetInfo.target.triple).description))

if let targetVariantTriple = frontendTargetInfo.targetVariant?.triple {
commandLine.append(.flag("-target-variant-sdk-version"))
commandLine.append(.flag(sdkInfo.sdkVersion(for: targetVariantTriple).description))
}
}
}

extension Diagnostic.Message {
Expand Down
12 changes: 12 additions & 0 deletions Sources/SwiftDriver/Toolchains/Toolchain.swift
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,12 @@ public protocol Toolchain {
parsedOptions: inout ParsedOptions,
sdkPath: String?,
targetTriple: Triple) throws -> [String: String]

func addPlatformSpecificCommonFrontendOptions(
commandLine: inout [Job.ArgTemplate],
inputs: inout [TypedVirtualPath],
frontendTargetInfo: FrontendTargetInfo
) throws
}

extension Toolchain {
Expand Down Expand Up @@ -156,6 +162,12 @@ extension Toolchain {
public func validateArgs(_ parsedOptions: inout ParsedOptions,
targetTriple: Triple,
targetVariantTriple: Triple?, diagnosticsEngine: DiagnosticsEngine) {}

public func addPlatformSpecificCommonFrontendOptions(
commandLine: inout [Job.ArgTemplate],
inputs: inout [TypedVirtualPath],
frontendTargetInfo: FrontendTargetInfo
) throws {}
}

public enum ToolchainError: Swift.Error {
Expand Down
60 changes: 60 additions & 0 deletions Sources/SwiftDriver/Utilities/VersionExtensions.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
//===---------- VersionExtensions.swift - Version Parsing Utilities -------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2020 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

import TSCUtility

// TODO: maybe move this to TSC.
extension Version {
/// Create a version from a string, replacing unknown trailing components with '0'.
init?(potentiallyIncompleteVersionString string: String) {
// This is a copied version of TSC's version parsing, modified to fill
// in missing components if needed.
let prereleaseStartIndex = string.firstIndex(of: "-")
let metadataStartIndex = string.firstIndex(of: "+")

let requiredEndIndex = prereleaseStartIndex ?? metadataStartIndex ?? string.endIndex
let requiredCharacters = string.prefix(upTo: requiredEndIndex)
var requiredComponents = requiredCharacters
.split(separator: ".", maxSplits: 2, omittingEmptySubsequences: false)
.map(String.init).compactMap({ Int($0) }).filter({ $0 >= 0 })

requiredComponents.append(contentsOf:
Array(repeating: 0,
count: max(0, 3 - requiredComponents.count)))

let major = requiredComponents[0]
let minor = requiredComponents[1]
let patch = requiredComponents[2]

func identifiers(start: String.Index?, end: String.Index) -> [String] {
guard let start = start else { return [] }
let identifiers = string[string.index(after: start)..<end]
return identifiers.split(separator: ".").map(String.init)
}

let prereleaseIdentifiers = identifiers(
start: prereleaseStartIndex,
end: metadataStartIndex ?? string.endIndex)
let buildMetadataIdentifiers = identifiers(
start: metadataStartIndex,
end: string.endIndex)

self.init(major, minor, patch,
prereleaseIdentifiers: prereleaseIdentifiers,
buildMetadataIdentifiers: buildMetadataIdentifiers)
}

/// Returns the version with out any build/release metadata numbers.
var withoutBuildNumbers: Version {
return Version(self.major, self.minor, self.patch)
}
}
118 changes: 118 additions & 0 deletions Tests/SwiftDriverTests/SwiftDriverTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1608,6 +1608,124 @@ final class SwiftDriverTests: XCTestCase {
try assertNoDriverDiagnostics(args: "swiftc", "-c", "-target", "x86_64-apple-macosx10.14", "-link-objc-runtime", "foo.swift")
}

// Test cases ported from Driver/macabi-environment.swift
func testDarwinSDKVersioning() throws {
try withTemporaryDirectory { tmpDir in
let sdk1 = tmpDir.appending(component: "MacOSX10.15.versioned.sdk")
try localFileSystem.writeFileContents(sdk1.appending(component: "SDKSettings.json")) {
$0 <<< """
{
"Version":"10.15",
"VersionMap" : {
"macOS_iOSMac" : {
"10.15" : "13.1",
"10.15.1" : "13.2"
},
"iOSMac_macOS" : {
"13.1" : "10.15",
"13.2" : "10.15.1"
}
}
}
"""
}

let sdk2 = tmpDir.appending(component: "MacOSX10.15.4.versioned.sdk")
try localFileSystem.writeFileContents(sdk2.appending(component: "SDKSettings.json")) {
$0 <<< """
{
"Version":"10.15.4",
"VersionMap" : {
"macOS_iOSMac" : {
"10.14.4" : "12.4",
"10.14.3" : "12.3",
"10.14.2" : "12.2",
"10.14.1" : "12.1",
"10.15" : "13.0",
"10.14" : "12.0",
"10.14.5" : "12.5",
"10.15.1" : "13.2",
"10.15.4" : "13.4"
},
"iOSMac_macOS" : {
"13.0" : "10.15",
"12.3" : "10.14.3",
"12.0" : "10.14",
"12.4" : "10.14.4",
"12.1" : "10.14.1",
"12.5" : "10.14.5",
"12.2" : "10.14.2",
"13.2" : "10.15.1",
"13.4" : "10.15.4"
}
}
}
"""
}

do {
var driver = try Driver(args: ["swiftc",
"-target", "x86_64-apple-macosx10.14",
"-sdk", sdk1.description,
"foo.swift"])
let frontendJobs = try driver.planBuild()
XCTAssertEqual(frontendJobs[0].kind, .compile)
XCTAssertTrue(frontendJobs[0].commandLine.contains(subsequence: [
.flag("-target-sdk-version"),
.flag("10.15.0")
]))
}

do {
var driver = try Driver(args: ["swiftc",
"-target", "x86_64-apple-macosx10.14",
"-target-variant", "x86_64-apple-ios13.0-macabi",
"-sdk", sdk1.description,
"foo.swift"])
let frontendJobs = try driver.planBuild()
XCTAssertEqual(frontendJobs[0].kind, .compile)
XCTAssertTrue(frontendJobs[0].commandLine.contains(subsequence: [
.flag("-target-sdk-version"),
.flag("10.15.0"),
.flag("-target-variant-sdk-version"),
.flag("13.1.0")
]))
}

do {
var driver = try Driver(args: ["swiftc",
"-target", "x86_64-apple-macosx10.14",
"-target-variant", "x86_64-apple-ios13.0-macabi",
"-sdk", sdk2.description,
"foo.swift"])
let frontendJobs = try driver.planBuild()
XCTAssertEqual(frontendJobs[0].kind, .compile)
XCTAssertTrue(frontendJobs[0].commandLine.contains(subsequence: [
.flag("-target-sdk-version"),
.flag("10.15.4"),
.flag("-target-variant-sdk-version"),
.flag("13.4.0")
]))
}

do {
var driver = try Driver(args: ["swiftc",
"-target-variant", "x86_64-apple-macosx10.14",
"-target", "x86_64-apple-ios13.0-macabi",
"-sdk", sdk2.description,
"foo.swift"])
let frontendJobs = try driver.planBuild()
XCTAssertEqual(frontendJobs[0].kind, .compile)
XCTAssertTrue(frontendJobs[0].commandLine.contains(subsequence: [
.flag("-target-sdk-version"),
.flag("13.4.0"),
.flag("-target-variant-sdk-version"),
.flag("10.15.4")
]))
}
}
}

func testDSYMGeneration() throws {
let commonArgs = [
"swiftc", "foo.swift", "bar.swift",
Expand Down