Skip to content

Commit e6e8487

Browse files
Dependencies: support optional module dependencies
1 parent 632eb45 commit e6e8487

File tree

5 files changed

+18
-19
lines changed

5 files changed

+18
-19
lines changed

Sources/SWBCore/Dependencies.swift

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import SWBMacro
1616
public struct ModuleDependency: Hashable, Sendable, SerializableCodable {
1717
public let name: String
1818
public let accessLevel: AccessLevel
19+
public let optional: Bool
1920

2021
public enum AccessLevel: String, Hashable, Sendable, CaseIterable, Codable, Serializable, Comparable {
2122
case Private = "private"
@@ -43,29 +44,25 @@ public struct ModuleDependency: Hashable, Sendable, SerializableCodable {
4344
}
4445
}
4546

46-
public init(name: String, accessLevel: AccessLevel) {
47+
public init(name: String, accessLevel: AccessLevel, optional: Bool) {
4748
self.name = name
4849
self.accessLevel = accessLevel
50+
self.optional = optional
4951
}
5052

5153
public init(entry: String) throws {
52-
var it = entry.split(separator: " ").makeIterator()
53-
switch (it.next(), it.next(), it.next()) {
54-
case (let .some(name), nil, nil):
55-
self.name = String(name)
56-
self.accessLevel = .Private
57-
58-
case (let .some(accessLevel), let .some(name), nil):
59-
self.name = String(name)
60-
self.accessLevel = try AccessLevel(String(accessLevel))
61-
62-
default:
63-
throw StubError.error("expected 1 or 2 space-separated components in: \(entry)")
54+
let re = #/((?<accessLevel>private|package|public)\s+)?(?<name>\w+)(?<optional>\?)?/#
55+
guard let match = entry.wholeMatch(of: re) else {
56+
throw StubError.error("Invalid module dependency format: \(entry), expected [private|package|public] <name>[?]")
6457
}
58+
59+
self.name = String(match.output.name)
60+
self.accessLevel = try match.output.accessLevel.map { try AccessLevel(String($0)) } ?? .Private
61+
self.optional = match.output.optional != nil
6562
}
6663

6764
public var asBuildSettingEntry: String {
68-
"\(accessLevel == .Private ? "" : "\(accessLevel.rawValue) ")\(name)"
65+
"\(accessLevel == .Private ? "" : "\(accessLevel.rawValue) ")\(name)\(optional ? "?" : "")"
6966
}
7067

7168
public var asBuildSettingEntryQuotedIfNeeded: String {
@@ -121,7 +118,7 @@ public struct ModuleDependenciesContext: Sendable, SerializableCodable {
121118

122119
public func computeUnusedDependencies(usedModuleNames: Set<String>) -> [ModuleDependency] {
123120
guard validateUnused != .no else { return [] }
124-
return moduleDependencies.filter { !usedModuleNames.contains($0.name) }
121+
return moduleDependencies.filter { !$0.optional && !usedModuleNames.contains($0.name) }
125122
}
126123

127124
/// Make diagnostics for missing module dependencies.

Sources/SWBCore/LibSwiftDriver/LibSwiftDriver.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -891,5 +891,6 @@ extension ModuleDependency {
891891
init(_ importInfo: ImportInfo) {
892892
self.name = importInfo.importIdentifier
893893
self.accessLevel = .init(importInfo.accessLevel)
894+
self.optional = false
894895
}
895896
}

Sources/SWBTaskExecution/TaskActions/ClangCompileTaskAction.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -388,7 +388,7 @@ public final class ClangCompileTaskAction: TaskAction, BuildValueValidatingTaskA
388388
includeFiles.append(file)
389389
}
390390
}
391-
let moduleDependencies = moduleNames.map { ModuleDependency(name: $0, accessLevel: .Private) }
391+
let moduleDependencies = moduleNames.map { ModuleDependency(name: $0, accessLevel: .Private, optional: false) }
392392
let moduleImports = moduleDependencies.map { DependencyValidationInfo.Import(dependency: $0, importLocations: []) }
393393
return (moduleImports, includeFiles)
394394
}

Sources/SWBTaskExecution/TaskActions/ValidateDependenciesTaskAction.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ public final class ValidateDependenciesTaskAction: TaskAction {
7979
let updatedSwiftMissingDeps: [(ModuleDependency, importLocations: [Diagnostic.Location])] = swiftMissingDeps.map {
8080
if let clangMissingDep = clangMissingDepsByName[$0.0.name] {
8181
return (
82-
ModuleDependency(name: $0.0.name, accessLevel: max($0.0.accessLevel, clangMissingDep.0.accessLevel)),
82+
ModuleDependency(name: $0.0.name, accessLevel: max($0.0.accessLevel, clangMissingDep.0.accessLevel), optional: $0.0.optional),
8383
$0.importLocations + clangMissingDep.importLocations
8484
)
8585
} else {

Tests/SWBBuildSystemTests/DependencyValidationTests.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -763,7 +763,7 @@ fileprivate struct DependencyValidationTests: CoreBasedTests {
763763
try await tester.fs.writeFileContents(projectXCConfigPath) { stream in
764764
stream <<<
765765
"""
766-
MODULE_DEPENDENCIES[target=TargetA] = CoreFoundation Foundation AppKit
766+
MODULE_DEPENDENCIES[target=TargetA] = CoreFoundation Foundation AppKit UIKit?
767767
"""
768768
}
769769

@@ -774,7 +774,8 @@ fileprivate struct DependencyValidationTests: CoreBasedTests {
774774
try await tester.checkBuild(runDestination: .host, buildRequest: buildRequest, persistent: true) { results in
775775
guard !results.checkWarning(.prefix("The current toolchain does not support VALIDATE_MODULE_DEPENDENCIES"), failIfNotFound: false) else { return }
776776

777-
results.checkWarning(.contains("Unused entries in MODULE_DEPENDENCIES: AppKit"))
777+
// This diagnostic should only report AppKit because UIKit is marked optional.
778+
results.checkWarning(.equal("Unused entries in MODULE_DEPENDENCIES: AppKit (for task: [\"ValidateDependencies\"])"))
778779
}
779780
}
780781
}

0 commit comments

Comments
 (0)