Skip to content

Commit 1a7cde9

Browse files
authored
Merge pull request #1051 from nkcsgexi/diagnose-api-level
AdopterAnalysis: diagnose several issues within .swiftinterface
2 parents 0c4d926 + e3b39e7 commit 1a7cde9

File tree

2 files changed

+84
-7
lines changed

2 files changed

+84
-7
lines changed

Sources/SwiftDriver/Jobs/PrebuiltModulesJob.swift

Lines changed: 61 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,45 @@ import TSCBasic
1313
import SwiftOptions
1414
import Foundation
1515

16-
@_spi(Testing) public func isIosMacInterface(_ path: VirtualPath) throws -> Bool {
16+
func getModuleFlags(_ path: VirtualPath, _ ignorable: Bool) throws -> [String] {
1717
let data = try localFileSystem.readFileContents(path).cString
1818
let myStrings = data.components(separatedBy: .newlines)
19-
let prefix = "// swift-module-flags: "
19+
let prefix = ignorable ? "// swift-module-flags-ignorable: " : "// swift-module-flags: "
2020
if let argLine = myStrings.first(where: { $0.hasPrefix(prefix) }) {
21-
let args = argLine.dropFirst(prefix.count).components(separatedBy: " ")
22-
if let idx = args.firstIndex(of: "-target"), idx + 1 < args.count {
23-
return args[idx + 1].contains("macabi")
24-
}
21+
return argLine.dropFirst(prefix.count).components(separatedBy: " ")
22+
}
23+
return []
24+
}
25+
26+
@_spi(Testing) public func getAllModuleFlags(_ path: VirtualPath) throws -> [String] {
27+
var allFlags: [String] = []
28+
allFlags.append(contentsOf: try getModuleFlags(path, true))
29+
allFlags.append(contentsOf: try getModuleFlags(path, false))
30+
return allFlags;
31+
}
32+
33+
@_spi(Testing) public func isIosMacInterface(_ path: VirtualPath) throws -> Bool {
34+
let args = try getAllModuleFlags(path)
35+
if let idx = args.firstIndex(of: "-target"), idx + 1 < args.count {
36+
return args[idx + 1].contains("macabi")
2537
}
2638
return false
2739
}
2840

41+
@_spi(Testing) public enum LibraryLevel: String, Codable {
42+
case api
43+
case spi
44+
case unknown
45+
case unspecified
46+
}
47+
48+
@_spi(Testing) public func getLibraryLevel(_ flags: [String]) throws -> LibraryLevel {
49+
if let idx = flags.firstIndex(of: "-library-level"), idx + 1 < flags.count {
50+
return LibraryLevel(rawValue: flags[idx + 1]) ?? .unknown
51+
}
52+
return .unspecified
53+
}
54+
2955
enum ErrKind: String {
3056
case err
3157
case warn
@@ -35,7 +61,7 @@ enum ErrKind: String {
3561
fileprivate func getErrKind(_ content: String) -> ErrKind {
3662
if content.contains("error: ") {
3763
return .err
38-
} else if content.contains("warning: "){
64+
} else if content.contains("warning: ") {
3965
return .warn
4066
} else {
4167
return .note
@@ -250,6 +276,12 @@ public struct PrebuiltModuleInput {
250276
}
251277
}
252278

279+
public enum AdopterIssueKind: String, Codable {
280+
case libraryEvolutionDisabled
281+
case libraryLevelMissing
282+
case libraryLevelWrong
283+
}
284+
253285
public class SwiftAdopter: Codable {
254286
public let name: String
255287
public let moduleDir: String
@@ -260,6 +292,7 @@ public class SwiftAdopter: Codable {
260292
public let isPrivate: Bool
261293
public let hasCompatibilityHeader: Bool
262294
public let isMixed: Bool
295+
public let issues: [AdopterIssueKind]?
263296
init(_ name: String, _ moduleDir: AbsolutePath, _ hasInterface: [AbsolutePath], _ hasModule: [AbsolutePath]) throws {
264297
self.name = name
265298
self.moduleDir = SwiftAdopter.relativeToSDK(moduleDir)
@@ -271,6 +304,27 @@ public class SwiftAdopter: Codable {
271304
let headers = try SwiftAdopter.collectHeaderNames(moduleDir.parentDirectory.parentDirectory)
272305
self.hasCompatibilityHeader = headers.contains { $0 == "\(name)-Swift.h" }
273306
self.isMixed = headers.contains { $0 != "\(name)-Swift.h" }
307+
self.issues = try Self.collectModuleIssues(hasInterface.first, self.isPrivate)
308+
}
309+
310+
static func collectModuleIssues(_ interface: AbsolutePath?, _ isPrivate: Bool) throws -> [AdopterIssueKind]? {
311+
guard let interface = interface else { return nil }
312+
var issues: [AdopterIssueKind] = []
313+
let flags = try getAllModuleFlags(VirtualPath.absolute(interface))
314+
let libLevel = try getLibraryLevel(flags)
315+
if libLevel == .unspecified {
316+
issues.append(.libraryLevelMissing)
317+
}
318+
if libLevel == .spi && !isPrivate {
319+
issues.append(.libraryLevelWrong)
320+
}
321+
if libLevel == .api && isPrivate {
322+
issues.append(.libraryLevelWrong)
323+
}
324+
if !flags.contains("-enable-library-evolution") {
325+
issues.append(.libraryEvolutionDisabled)
326+
}
327+
return issues.isEmpty ? nil : issues
274328
}
275329

276330
static func collectHeaderNames(_ headersIn: AbsolutePath) throws -> [String] {

Tests/SwiftDriverTests/SwiftDriverTests.swift

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5943,6 +5943,29 @@ final class SwiftDriverTests: XCTestCase {
59435943
}
59445944
}
59455945

5946+
func testExtractLibraryLevel() throws {
5947+
try withTemporaryFile { file in
5948+
try localFileSystem.writeFileContents(file.path) { $0 <<< "// swift-module-flags: -library-level api" }
5949+
let flags = try getAllModuleFlags(VirtualPath.absolute(file.path))
5950+
XCTAssertEqual(try getLibraryLevel(flags), .api)
5951+
}
5952+
try withTemporaryFile { file in
5953+
try localFileSystem.writeFileContents(file.path) {
5954+
$0 <<< "// swift-module-flags: -target arm64e-apple-macos12.0" <<< "\n"
5955+
$0 <<< "// swift-module-flags-ignorable: -library-level spi"
5956+
}
5957+
let flags = try getAllModuleFlags(VirtualPath.absolute(file.path))
5958+
XCTAssertEqual(try getLibraryLevel(flags), .spi)
5959+
}
5960+
try withTemporaryFile { file in
5961+
try localFileSystem.writeFileContents(file.path) {
5962+
$0 <<< "// swift-module-flags: -target arm64e-apple-macos12.0"
5963+
}
5964+
let flags = try getAllModuleFlags(VirtualPath.absolute(file.path))
5965+
XCTAssertEqual(try getLibraryLevel(flags), .unspecified)
5966+
}
5967+
}
5968+
59465969
func testSupportedFeatureJson() throws {
59475970
let driver = try Driver(args: ["swiftc", "-emit-module", "foo.swift"])
59485971
XCTAssertFalse(driver.supportedFrontendFeatures.isEmpty)

0 commit comments

Comments
 (0)