File: BuildFileResolution.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 (117 lines) | stat: -rw-r--r-- 8,143 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
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

public import SWBUtil
import SWBMacro

/// Protocol to support getting resolved information about a `BuildFile` within a `ConfiguredTarget`.
public protocol BuildFileResolution: SpecLookupContext {
    var workspaceContext: WorkspaceContext { get }
    var configuredTarget: ConfiguredTarget? { get }
    var settings: Settings { get }
    var globalTargetInfoProvider: any GlobalTargetInfoProvider { get }
}

extension BuildFileResolution {
    public var specRegistry: SpecRegistry {
        return platform?.specRegistry ?? workspaceContext.core.specRegistry
    }
}

/// Protocol to provide information about targets within the context of a build.
/// - remark: This is usually the `GlobalProductPlan` for the build. See that class for info about the characteristics of this protocol.
public protocol GlobalTargetInfoProvider {
    func getTargetSettings(_ configuredTarget: ConfiguredTarget) -> Settings
    func dependencies(of target: ConfiguredTarget) -> [ConfiguredTarget]
    var dynamicallyBuildingTargets: Set<Target> { get }
}

extension BuildFileResolution {

    /// Return the `Settings` for the given product reference `Target` in the target dependency graph of the current build.
    /// This is important if the producing target has context-dependent overrides which are important to the caller, rather than just applying the current configured target's parameters to the producing target.
    /// - remark: If the producing target is a dependency of the current target (likely, but not guaranteed), then the `Settings` will be looked up from the `TargetInfoProvider` (which is likely the `GlobalProductPlan`).  Otherwise a more complicated heuristic is used.
    public func settingsForProductReferenceTarget(_ productRefTarget: Target, parameters: BuildParameters) -> Settings {
        // If we have a concrete client target, we can look up the exact configured target that is producing the referenced product.
        if let clientConfiguredTarget = self.configuredTarget, let productRefConfiguredTarget = globalTargetInfoProvider.dependencies(of: clientConfiguredTarget).filter({ $0.target.guid == productRefTarget.guid }).only {
            return globalTargetInfoProvider.getTargetSettings(productRefConfiguredTarget)
        } else {
            // FIXME: A more reliable fallback might be to use GlobalProductPlan.productPathsToProducingTargets (where GlobalProductPlan conforms to TargetInfoProvider) if the absolute path of the product reference in this context were passed in.
            // If the reference is a product reference, then we want to use the settings for the configured target which produced it in this build, as it may have been built for a different platform and so we need to look up its information in the context of that platform.
            // FIXME: This is potentially unsound since there's no inherent guarantee that this configured target exists in the build graph, but I think in practice it should work?
            let potentialSettings = globalTargetInfoProvider.getTargetSettings(ConfiguredTarget(parameters: parameters, target: productRefTarget))
            // If the `productRefTarget` needs specialization, we need to override `SDKROOT`.
            // FIXME: Ideally, we would be in a position where we can just use the right configured target here.
            if potentialSettings.enableTargetPlatformSpecialization {
                var overrides = parameters.overrides
                overrides["SDKROOT"] = settings.globalScope.evaluate(BuiltinMacros.SDKROOT).str
                overrides["SUPPORTED_PLATFORMS"] = settings.globalScope.evaluate(BuiltinMacros.SUPPORTED_PLATFORMS).joined(separator: " ")
                overrides["SDK_VARIANT"] = settings.globalScope.evaluate(BuiltinMacros.SDK_VARIANT)
                if overrides["SDK_VARIANT"] != MacCatalystInfo.sdkVariantName {
                    overrides["SUPPORTS_MACCATALYST"] = "NO"
                }
                overrides["TOOLCHAINS"] = settings.globalScope.evaluate(BuiltinMacros.TOOLCHAINS).joined(separator: " ")
                return globalTargetInfoProvider.getTargetSettings(ConfiguredTarget(parameters: parameters.mergingOverrides(overrides), target: productRefTarget))
            } else {
                return potentialSettings
            }
        }
    }

    /// Resolve the information for a `BuildFile`.
    /// - returns: The concrete reference being built, its resolved absolute path, and its type.
    public func resolveBuildFileReference(_ buildFile: BuildFile, reference: Reference? = nil) throws -> (reference: Reference, absolutePath: Path, fileType: FileTypeSpec) {
        let (reference, _, absolutePath, fileType) = try resolveBuildFileReference(buildFile, reference: reference)
        return (reference, absolutePath, fileType)
    }

    /// Resolve the information for a `BuildFile`.
    /// - returns: The concrete reference being built, the `Settings` of its associated target, its resolved absolute path, and its type.
    /// - remark: This is a disfavored older version, as most clients don't need the `Settings` object.
    @_disfavoredOverload public func resolveBuildFileReference(_ buildFile: BuildFile, reference: Reference? = nil) throws -> (reference: Reference, settings: Settings, absolutePath: Path, fileType: FileTypeSpec) {
        let reference = try reference ?? workspaceContext.workspace.resolveBuildableItemReference(buildFile.buildableItem, dynamicallyBuildingTargets: globalTargetInfoProvider.dynamicallyBuildingTargets)
        let settingsForRef: Settings
        let specLookupContext: any SpecLookupContext
        switch reference {
        case let productRef as ProductReference:
            if let productRefTarget = productRef.target, let parameters = configuredTarget?.parameters {
                settingsForRef = settingsForProductReferenceTarget(productRefTarget, parameters: parameters)
                specLookupContext = SpecLookupCtxt(specRegistry: settingsForRef.platform?.specRegistry ?? workspaceContext.core.specRegistry, platform: settingsForRef.platform)
            }
            else {
                // If the product reference doesn't have a producing target, or we don't have a configured target, then... that's very weird.
                settingsForRef = settings
                specLookupContext = self
            }
        default:
            settingsForRef = settings
            specLookupContext = self
        }

        // Resolve the path and file type.
        let absolutePath: Path, fileType: FileTypeSpec?
        switch reference {
            // Variant groups always resolve the path and file type of the first reference.
            // FIXME: This is historical, and should be cleaned up by making the input model more explicit. This also isn't exactly what Xcode would do, which is very risky. It is possible that we should extend Xcode to pass this information down with the top-level variant group itself. (This FIXME is from 2017 and was ported from TaskProducer.)
        case let asVariantGroup as VariantGroup where !asVariantGroup.children.isEmpty:
            absolutePath = settingsForRef.filePathResolver.resolveAbsolutePath(asVariantGroup.children[0])
            fileType = specLookupContext.lookupFileType(reference: asVariantGroup.children[0])
        default:
            absolutePath = settingsForRef.filePathResolver.resolveAbsolutePath(reference)
            fileType = specLookupContext.lookupFileType(reference: reference)
        }

        return (reference, settingsForRef, absolutePath, fileType ?? specLookupContext.lookupFileType(identifier: "file")!)
    }

}