File: BuildDescriptionSignature.swift

package info (click to toggle)
swiftlang 6.2.3-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 2,856,264 kB
  • sloc: cpp: 9,995,718; ansic: 2,234,019; asm: 1,092,167; python: 313,940; objc: 82,726; f90: 80,126; lisp: 38,373; pascal: 25,580; sh: 20,378; ml: 5,058; perl: 4,751; makefile: 4,725; awk: 3,535; javascript: 3,018; xml: 918; fortran: 664; cs: 573; ruby: 396
file content (167 lines) | stat: -rw-r--r-- 7,775 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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift open source project
//
// Copyright (c) 2025 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 Foundation
@_spi(BuildDescriptionSignatureComponents) import SWBCore
package import SWBTaskConstruction
package import SWBUtil
import struct SWBProtocol.BuildDescriptionID

/// The type of the signature for a build description.
package typealias BuildDescriptionSignature = ByteString

/// Represents the components of a build description signature.
///
/// Any difference between two ``BuildDescriptionSignatureComponents`` instances indicates that the build description should be recomputed.
package struct BuildDescriptionSignatureComponents: Codable, Hashable, Sendable {
    enum BuildCommandCategory: Codable, Hashable, Sendable {
        case preprocess
        case assemble
        case other
    }

    struct TargetMetadata: Codable, Hashable, Sendable {
        let name: String
        let signature: String
        let buildParameters: BuildParameters
        let provisioningInputs: ProvisioningTaskInputs
        let macroConfigSignature: FilesSignature
        let specializeGuidForActiveRunDestination: Bool
    }

    struct ProjectMetadata: Codable, Hashable, Sendable {
        let name: String
        let macroConfigSignature: FilesSignature
    }

    struct SDKMetadata: Codable, Hashable, Sendable {
        let canonicalName: String
        let productBuildVersion: String?
    }

    let workspaceSignature: String
    let buildRequestParameters: BuildParameters
    let useParallelTargets: Bool
    let useImplicitDependencies: Bool
    let buildCommandCategory: BuildCommandCategory
    let enableStaleFileRemoval: Bool
    let targets: [TargetMetadata]
    let projects: [ProjectMetadata]
    let systemInfo: SystemInfo?
    let userInfo: UserInfo?
    let developerPath: Path
    let xcodeVersionString: String
    let xcodeProductBuildVersionString: String
    let buildServiceModTime: Date
    let sdkVersions: [SDKMetadata]

    fileprivate init(_ request: BuildPlanRequest) {
        workspaceSignature = request.workspaceContext.workspace.signature
        buildRequestParameters = request.buildRequest.parameters
        useParallelTargets = request.buildRequest.useParallelTargets
        useImplicitDependencies = request.buildRequest.useImplicitDependencies
        switch request.buildRequest.buildCommand {
        case .generatePreprocessedFile:
            buildCommandCategory = .preprocess
        case .generateAssemblyCode:
            buildCommandCategory = .assemble
        default:
            buildCommandCategory = .other
        }
        enableStaleFileRemoval = request.buildRequest.buildCommand.shouldEnableStaleFileRemoval
        targets = request.buildGraph.allTargets.map {
            TargetMetadata(
                name: $0.target.name,
                signature: $0.target.signature,
                buildParameters: $0.parameters,
                provisioningInputs: request.provisioningInputs(for: $0),
                macroConfigSignature: request.buildRequestContext.getCachedSettings($0.parameters, target: $0.target).macroConfigSignature,
                specializeGuidForActiveRunDestination: $0.specializeGuidForActiveRunDestination)
        }
        projects = request.workspaceContext.workspace.projects.map {
            ProjectMetadata(
                name: $0.name,
                macroConfigSignature: request.buildRequestContext.getCachedSettings(request.buildRequest.parameters, project: $0).macroConfigSignature)
        }
        systemInfo = request.workspaceContext.systemInfo
        userInfo = request.workspaceContext.userInfo
        developerPath = request.workspaceContext.core.developerPath.path
        xcodeVersionString = request.workspaceContext.core.xcodeVersionString
        xcodeProductBuildVersionString = request.workspaceContext.core.xcodeProductBuildVersionString
        buildServiceModTime = request.workspaceContext.core.buildServiceModTime

        // Add the ProductBuildVersion of installed SDKs, in case they are updated independently of Xcode
        sdkVersions = request.workspaceContext.core.sdkRegistry.allSDKs.sorted(by: \.canonicalName).map {
            SDKMetadata(canonicalName: $0.canonicalName, productBuildVersion: $0.productBuildVersion)
        }
    }
}

extension BuildDescriptionSignatureComponents {
    var humanReadableString: ByteString {
        get throws {
            try ByteString(JSONEncoder(outputFormatting: [.prettyPrinted, .sortedKeys, .withoutEscapingSlashes]).encode(self))
        }
    }

    func signatureStringValue(humanReadableString: ByteString) -> BuildDescriptionSignature {
        let hashContext = InsecureHashContext()
        hashContext.add(bytes: humanReadableString)
        return hashContext.signature
    }
}

extension BuildDescriptionSignature {
    /// Compare data that is used to compute the build description signature of two build plan requests and return a string
    /// with the list of differences or `nil` if they are equal.
    static func compareBuildDescriptionSignatures(_ request: BuildPlanRequest, _ otherRequest: BuildPlanRequest, _ cacheDir: Path) throws -> (previousSignaturePath: Path, currentSignaturePath: Path)? {
        let requestComponents = BuildDescriptionSignatureComponents(request)
        let otherRequestComponents = BuildDescriptionSignatureComponents(otherRequest)
        if requestComponents == otherRequestComponents {
            return nil
        }

        let fs = request.workspaceContext.fs
        let tempDir = try fs.createTemporaryDirectory(parent: fs.realpath(Path.temporaryDirectory))

        func write(_ components: BuildDescriptionSignatureComponents, _ name: String) throws -> Path {
            let path = tempDir.join("\(name).signature")
            try request.workspaceContext.fs.write(path, contents: components.humanReadableString)
            return path
        }

        return try (
            previousSignaturePath: write(otherRequestComponents, "previous"),
            currentSignaturePath: write(requestComponents, "current")
        )
    }

    /// Returns the signature to use to cache a build description for a particular workspace and request.
    package static func buildDescriptionSignature(_ request: BuildPlanRequest, cacheDir: Path) throws -> BuildDescriptionSignature {
        let signatureComponents = BuildDescriptionSignatureComponents(request)
        let humanReadableString = try signatureComponents.humanReadableString
        let signature = signatureComponents.signatureStringValue(humanReadableString: humanReadableString)

        if request.workspaceContext.userPreferences.enableDebugActivityLogs {
            let detailsPath = BuildDescription.buildDescriptionPackagePath(inDir: cacheDir, signature: signature).join("description.signature")
            try request.workspaceContext.fs.createDirectory(detailsPath.dirname, recursive: true)
            try request.workspaceContext.fs.write(detailsPath, contents: humanReadableString)
        }

        return signature
    }

    /// Returns the signature to use for a build description for a particular build description ID.
    static func buildDescriptionSignature(_ buildDescriptionID: BuildDescriptionID) -> BuildDescriptionSignature {
        return BuildDescriptionSignature(encodingAsUTF8: buildDescriptionID.rawValue)
    }
}