File: FileToBuild.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 (182 lines) | stat: -rw-r--r-- 9,162 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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
//===----------------------------------------------------------------------===//
//
// 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
public import SWBProtocol
public import SWBMacro

/// Represents a file to be passed as input to some part of the build machinery.  May be a source file originally sent down with the PIF, or might be a temporary file.  Once a build rule action has been determined, it is assigned to the FileToBuild so it doesn’t have to be looked up again.  Note that the term “file” here is used in the loosest sense — the path can refer to any file system entity.
public struct FileToBuild : Hashable {
    /// Absolute path of the referenced file.
    public let absolutePath: Path

    /// File type.
    public let fileType: FileTypeSpec

    /// The build file for the referenced file, if any.
    public let buildFile: BuildFile?

    /// Header visibility of the file, if specified.  This is derived from the build file.
    public var headerVisibility: HeaderVisibility? {
        return buildFile?.headerVisibility
    }

    /// Additional user-specified command line arguments to pass to the build tool, if specified.
    public var additionalArgs: MacroStringListExpression?

    /// The region for this file.
    ///
    /// This is either the explicit regionVariantName or the region name from the backing build file.
    public let regionVariantName: String?

    /// Mig interfaces to generate, if specified.
    public var migCodegenFiles: MigCodegenFiles? {
        return buildFile?.migCodegenFiles
    }

    /// Whether to generate interfaces for intents files
    public var intentsCodegenVisibility: IntentsCodegenVisibility? {
        return buildFile?.intentsCodegenVisibility
    }

    /// Whether to code sign the file when copied, if specified.
    public var codeSignOnCopy: Bool {
        return buildFile?.codeSignOnCopy ?? false
    }

    /// Whether to remove headers when copied, if specified.
    public var removeHeadersOnCopy: Bool {
        return buildFile?.removeHeadersOnCopy ?? false
    }

    // FIXME: This attribute is really private to C compilers, and only used to prevent the versioning stub from using a PCH. It would be nice to find a more narrowly scoped mechanism for this.
    //
    /// Whether the file should use a prefix header, if appropriate.
    public let shouldUsePrefixHeader: Bool

    /// A suffix which can be used to define a unique basename for the file, in the context it is being built.
    public let uniquingSuffix: String

    /// If set, indexing info should use this path instead of `absolutePath`
    public let indexingInputReplacement: Path?

    /// Initializer.
    public init(absolutePath: Path, fileType: FileTypeSpec, buildFile: BuildFile? = nil, additionalArgs: MacroStringListExpression? = nil, uniquingSuffix: String = "", shouldUsePrefixHeader: Bool = true, regionVariantName: String? = nil, indexingInputReplacement: Path? = nil) {
        assert(absolutePath.isAbsolute, "path is not absolute: \(absolutePath)")
        assert(!(additionalArgs != nil && buildFile?.additionalArgs != nil), "unexpected explicit additional args and build file args")
        // We normalize the path here so this form is consistently available to CommandLineToolSpec instances.  The old build system normalized paths in almost all cases.
        self.absolutePath = absolutePath.normalize()
        self.indexingInputReplacement = indexingInputReplacement?.normalize()
        self.fileType = fileType
        self.buildFile = buildFile
        self.additionalArgs = additionalArgs ?? buildFile?.additionalArgs
        self.shouldUsePrefixHeader = shouldUsePrefixHeader
        self.uniquingSuffix = uniquingSuffix

        // Use the explicit region variant name or try to get it from the file path.
        self.regionVariantName = regionVariantName ?? absolutePath.regionVariantName
    }

    // FIXME: Figure out how this should look, maybe clients have to produce the fully formed output, instead of inferring information? We need to determine how often we know the exact file type in situations where we use this.
    //
    /// Convenience initializer, using inferred types.
    public init(absolutePath: Path, inferringTypeUsing specLookupContext: any SpecLookupContext, buildFile: BuildFile? = nil, additionalArgs: MacroStringListExpression? = nil, uniquingSuffix: String = "", shouldUsePrefixHeader: Bool = true, regionVariantName: String? = nil, indexingInputReplacement: Path? = nil) {
        let fileType = specLookupContext.lookupFileType(fileName: absolutePath.basename) ?? specLookupContext.lookupFileType(identifier: "file")!
        self.init(absolutePath: absolutePath, fileType: fileType, buildFile: buildFile, additionalArgs: additionalArgs, uniquingSuffix: uniquingSuffix, shouldUsePrefixHeader: shouldUsePrefixHeader, regionVariantName: regionVariantName, indexingInputReplacement: indexingInputReplacement)
    }

    public func hash(into hasher: inout Hasher) {
        hasher.combine(absolutePath)
        hasher.combine(fileType.identifier)
        hasher.combine(headerVisibility)
    }

    public static func ==(lhs: FileToBuild, rhs: FileToBuild) -> Bool {
        // QUESTION: Does the header visibility really make two files of the same type and the same path different? <rdar://problem/29980516> [Swift Build] Should FileToBuild.== be considering the headerVisibility property?
        return lhs.absolutePath == rhs.absolutePath && lhs.fileType === rhs.fileType && lhs.headerVisibility == rhs.headerVisibility
    }

    /// Fileprivate description to make FileToBuildGroup.description a bit more readable.
    fileprivate var descriptionForGroup: String {
        return "\(type(of: self))(absolutePath: \(absolutePath.str), fileType: \(fileType.identifier):\(fileType.domain == "" ? "\"\"" : fileType.domain), buildFile: \(buildFile?.description ?? "nil") additionalArgs: \(additionalArgs?.description ?? "nil"), regionVariantName: \(regionVariantName?.description ?? "nil"), shouldUsePrefixHeader: \(shouldUsePrefixHeader), uniquingSuffix: \(uniquingSuffix.isEmpty ? "\"\"" : uniquingSuffix), indexingInputReplacement: \(indexingInputReplacement?.str ?? "nil"))"
    }
}

/// Represents a group of one or more files to be processed by a single build task.  Once added to a group, a file cannot be removed from it.
public final class FileToBuildGroup : Hashable, Equatable, CustomStringConvertible {
    /// The identifier for this group.
    public let identifier: String?

    /// The files that are associated with this group.
    public var files: [FileToBuild]

    /// The assigned build rule action.
    public let assignedBuildRuleAction: (any BuildRuleAction)?

    public init(_ identifier: String? = nil, files: [FileToBuild] = [], action: (any BuildRuleAction)?) {
        self.identifier = identifier
        self.files = files
        self.assignedBuildRuleAction = action
    }

    public func hash(into hasher: inout Hasher) {
        hasher.combine(files)
    }

    public static func ==(lhs: FileToBuildGroup, rhs: FileToBuildGroup) -> Bool {
        return lhs.files == rhs.files
    }

    public var description: String
    {
        // Since only the files are relevant for equality, that's all we emit here.
        let filesDescription = files.map({ $0.descriptionForGroup }).joined(separator: ", ")
        return "<\(type(of: self)):[\(filesDescription)]>"
    }
}

public protocol RegionVariable {
    var regionVariantName: String? { get }
}

extension Path: RegionVariable { }
extension FileToBuild: RegionVariable { }

extension FileToBuildGroup: RegionVariable {
    /// Returns the region variant name for a group of files.
    ///
    /// This only potentially returns a region name if there is a single file, or if all files have the same region.
    /// This makes the result completely unambiguous and order-independent.
    public var regionVariantName: String? {
        return files.regionVariantName
    }
}

extension Array: RegionVariable where Array.Element == FileToBuild {
    public var regionVariantName: String? {
        return Set(map { $0.regionVariantName }).only ?? nil
    }
}

public extension RegionVariable {
    var regionVariantPathComponent: String {
        guard let regionVariantName else { return "" }
        return regionVariantName + ".lproj/"
    }

    /// This is only relevant for the installloc action for Localization projects.
    func isValidLocalizedContent(_ scope: MacroEvaluationScope) -> Bool {
        guard let regionVariantName else { return false }
        let installLocLanguages = Set(scope.evaluate(BuiltinMacros.INSTALLLOC_LANGUAGE))
        return installLocLanguages.isEmpty || installLocLanguages.contains(regionVariantName)
    }
}