File: Toolchain.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 (128 lines) | stat: -rw-r--r-- 5,921 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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift open source project
//
// Copyright (c) 2021 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 Foundation
import PackageModel
import Workspace

import struct TSCBasic.ByteString
import class Basics.AsyncProcess
import struct TSCBasic.StringError
import struct TSCUtility.SerializedDiagnostics

#if os(macOS)
private func macOSBundleRoot() throws -> AbsolutePath {
    for bundle in Bundle.allBundles where bundle.bundlePath.hasSuffix(".xctest") {
        return try AbsolutePath(validating: bundle.bundlePath).parentDirectory
    }
    fatalError()
}
#endif

private func resolveBinDir() throws -> AbsolutePath {
#if os(macOS)
    return try macOSBundleRoot()
#else
    return try AbsolutePath(validating: CommandLine.arguments[0], relativeTo: localFileSystem.currentWorkingDirectory!).parentDirectory
#endif
}

extension SwiftSDK {
    public static var `default`: Self {
        get throws {
            let binDir = try resolveBinDir()
            return try! SwiftSDK.hostSwiftSDK(binDir, environment: .current)
        }
    }
}

extension UserToolchain {
    public static var `default`: Self {
        get throws {
            return try .init(swiftSDK: SwiftSDK.default, environment: .current, fileSystem: localFileSystem)
        }
    }
}

extension UserToolchain {
    /// Helper function to determine if async await actually works in the current environment.
    public func supportsSwiftConcurrency() -> Bool {
      #if os(macOS)
        if #available(macOS 12.0, *) {
            // On macOS 12 and later, concurrency is assumed to work.
            return true
        }
        else {
            // On macOS 11 and earlier, we don't know if concurrency actually works because not all SDKs and toolchains have the right bits.  We could examine the SDK and the various libraries, but the most accurate test is to just try to compile and run a snippet of code that requires async/await support.  It doesn't have to actually do anything, it's enough that all the libraries can be found (but because the library reference is weak we do need the linkage reference to `_swift_task_create` and the like).
            do {
                try testWithTemporaryDirectory { tmpPath in
                    let inputPath = tmpPath.appending("foo.swift")
                    try localFileSystem.writeFileContents(inputPath, string: "public func foo() async {}\nTask { await foo() }")
                    let outputPath = tmpPath.appending("foo")
                    let toolchainPath = self.swiftCompilerPath.parentDirectory.parentDirectory
                    let backDeploymentLibPath = toolchainPath.appending(components: "lib", "swift-5.5", "macosx")
                    try AsyncProcess.checkNonZeroExit(arguments: ["/usr/bin/xcrun", "--toolchain", toolchainPath.pathString, "swiftc", inputPath.pathString, "-Xlinker", "-rpath", "-Xlinker", backDeploymentLibPath.pathString, "-o", outputPath.pathString])
                    try AsyncProcess.checkNonZeroExit(arguments: [outputPath.pathString])
                }
            } catch {
                // On any failure we assume false.
                return false
            }
            // If we get this far we could compile and run a trivial executable that uses libConcurrency, so we can say that this toolchain supports concurrency on this host.
            return true
        }
      #else
        // On other platforms, concurrency is assumed to work since with new enough versions of the toolchain.
        return true
      #endif
    }

    /// Helper function to determine whether serialized diagnostics work properly in the current environment.
    public func supportsSerializedDiagnostics(otherSwiftFlags: [String] = []) -> Bool {
        do {
            try testWithTemporaryDirectory { tmpPath in
                let inputPath = tmpPath.appending("best.swift")
                try localFileSystem.writeFileContents(inputPath, string: "func foo() -> Bool {\nvar unused: Int\nreturn true\n}\n")
                let outputPath = tmpPath.appending("foo")
                let serializedDiagnosticsPath = tmpPath.appending("out.dia")
                let toolchainPath = self.swiftCompilerPath.parentDirectory.parentDirectory
                try AsyncProcess.checkNonZeroExit(
                    arguments: [
                        "/usr/bin/xcrun", "--toolchain", toolchainPath.pathString,
                        "swiftc",
                        inputPath.pathString,
                        "-Xfrontend", "-serialize-diagnostics-path",
                        "-Xfrontend", serializedDiagnosticsPath.pathString,
                        "-g",
                        "-o", outputPath.pathString
                    ] + otherSwiftFlags
                )
                try AsyncProcess.checkNonZeroExit(arguments: [outputPath.pathString])

                let diaFileContents = try localFileSystem.readFileContents(serializedDiagnosticsPath)
                let diagnosticsSet = try SerializedDiagnostics(bytes: diaFileContents)
                if diagnosticsSet.diagnostics.isEmpty {
                    throw StringError("does not support diagnostics")
                }
            }
            return true
        } catch {
            return false
        }
    }

    /// Helper function to determine whether we should run SDK-dependent tests.
    public func supportsSDKDependentTests() -> Bool {
        return ProcessInfo.processInfo.environment["SWIFTCI_DISABLE_SDK_DEPENDENT_TESTS"] == nil
    }
}