File: MinimumDeploymentTarget.swift

package info (click to toggle)
swiftlang 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,519,992 kB
  • sloc: cpp: 9,107,863; ansic: 2,040,022; asm: 1,135,751; python: 296,500; objc: 82,456; f90: 60,502; lisp: 34,951; pascal: 19,946; sh: 18,133; perl: 7,482; ml: 4,937; javascript: 4,117; makefile: 3,840; awk: 3,535; xml: 914; fortran: 619; cs: 573; ruby: 573
file content (107 lines) | stat: -rw-r--r-- 4,518 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift open source project
//
// Copyright (c) 2014-2020 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

import Basics

import class Basics.AsyncProcess

public struct MinimumDeploymentTarget {
    private struct MinimumDeploymentTargetKey: Hashable {
        let binaryPath: AbsolutePath
        let platform: PackageModel.Platform
    }

    private let minimumDeploymentTargets = ThreadSafeKeyValueStore<MinimumDeploymentTargetKey,PlatformVersion>()
    private let xcTestMinimumDeploymentTargets = ThreadSafeKeyValueStore<PackageModel.Platform,PlatformVersion>()

    public static let `default`: MinimumDeploymentTarget = .init()

    private init() {
    }

    public func computeMinimumDeploymentTarget(of binaryPath: AbsolutePath, platform: PackageModel.Platform) throws -> PlatformVersion {
        try self.minimumDeploymentTargets.memoize(MinimumDeploymentTargetKey(binaryPath: binaryPath, platform: platform)) {
            return try Self.computeMinimumDeploymentTarget(of: binaryPath, platform: platform) ?? platform.oldestSupportedVersion
        }
    }

    public func computeXCTestMinimumDeploymentTarget(for platform: PackageModel.Platform) -> PlatformVersion {
        self.xcTestMinimumDeploymentTargets.memoize(platform) {
            return Self.computeXCTestMinimumDeploymentTarget(for: platform)
        }
    }

    static func computeMinimumDeploymentTarget(of binaryPath: AbsolutePath, platform: PackageModel.Platform) throws -> PlatformVersion? {
        guard let (_, platformName) = platform.sdkNameAndPlatform else {
            return nil
        }

        let runResult = try AsyncProcess.popen(arguments: ["/usr/bin/xcrun", "vtool", "-show-build", binaryPath.pathString])
        var lines = try runResult.utf8Output().components(separatedBy: "\n")
        while !lines.isEmpty {
            let first = lines.removeFirst()
            if first.contains("platform \(platformName)"), let line = lines.first, line.contains("minos") {
                return line.components(separatedBy: " ").last.map(PlatformVersion.init(stringLiteral:))
            }
        }
        return nil
    }

    static func computeXCTestMinimumDeploymentTarget(with runResult: AsyncProcessResult, platform: PackageModel.Platform) throws -> PlatformVersion? {
        guard let output = try runResult.utf8Output().spm_chuzzle() else { return nil }
        let sdkPath = try AbsolutePath(validating: output)
        let xcTestPath = try AbsolutePath(validating: "Developer/Library/Frameworks/XCTest.framework/XCTest", relativeTo: sdkPath)
        return try computeMinimumDeploymentTarget(of: xcTestPath, platform: platform)
    }

    static func computeXCTestMinimumDeploymentTarget(for platform: PackageModel.Platform) -> PlatformVersion {
        guard let (sdkName, _) = platform.sdkNameAndPlatform else {
            return platform.oldestSupportedVersion
        }

        // On macOS, we are determining the deployment target by looking at the XCTest binary.
        #if os(macOS)
        do {
            let runResult = try AsyncProcess.popen(arguments: ["/usr/bin/xcrun", "--sdk", sdkName, "--show-sdk-platform-path"])

            if let version = try computeXCTestMinimumDeploymentTarget(with: runResult, platform: platform) {
                return version
            }
        } catch { } // we do not treat this a fatal and instead use the fallback minimum deployment target
        #endif

        return platform.oldestSupportedVersion
    }
}

private extension PackageModel.Platform {
    var sdkNameAndPlatform: (String, String)? {
        switch self {
        case .macOS:
            return ("macosx", "MACOS")
        case .macCatalyst:
            return ("macosx", "MACCATALYST")
        case .iOS:
            return ("iphoneos", "IOS")
        case .tvOS:
            return ("appletvos", "TVOS")
        case .watchOS:
            return ("watchos", "WATCHOS")
        case .visionOS:
            return ("xros", "XROS")
        case .driverKit:
            return nil // DriverKit does not support XCTest.
        default:
            return nil
        }
    }
}