Skip to content

Commit 857250f

Browse files
jpsimcompnerd
andauthored
Merge pull request #779 from jpsim/jp-basic-windows
Add partial Windows support Co-authored-by: Saleem Abdulrasool <compnerd@compnerd.org>
2 parents fb64d42 + d01d708 commit 857250f

14 files changed

+441
-136
lines changed
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
import Foundation
2+
3+
// MARK: - LibraryWrapperGenerator
4+
5+
/// Generator for SourceKitten's library wrappers.
6+
enum LibraryWrapperGenerator: CaseIterable {
7+
case clang
8+
case sourcekit
9+
10+
/// The relative file path for this library wrapper's generated source file.
11+
var filePath: String {
12+
return "Source/SourceKittenFramework/library_wrapper_\(moduleName).swift"
13+
}
14+
15+
/// Generate the Swift source code for this library wrapper.
16+
///
17+
/// - parameter compilerArguments: The compiler arguments to SourceKittenFramework.
18+
///
19+
/// - returns: The generated Swift source code for this library wrapper.
20+
func generate(compilerArguments: [String]) throws -> String {
21+
let freeFunctions = try extractFreeFunctions(compilerArguments: compilerArguments)
22+
.joined(separator: "\n")
23+
return [
24+
fileHeader,
25+
"// swiftlint:disable unused_declaration - We don't care if some of these are unused.",
26+
freeFunctions,
27+
self == .clang ? "#endif" : nil
28+
]
29+
.compactMap { $0 }
30+
.joined(separator: "\n\n") + "\n"
31+
}
32+
}
33+
34+
// MARK: - Private
35+
36+
private extension LibraryWrapperGenerator {
37+
/// The wrapped module name.
38+
var moduleName: String {
39+
switch self {
40+
case .clang:
41+
return "Clang_C"
42+
case .sourcekit:
43+
return "SourceKit"
44+
}
45+
}
46+
47+
/// The top section of the generated library wrapper.
48+
var fileHeader: String {
49+
switch self {
50+
case .clang:
51+
return """
52+
#if !os(Linux)
53+
54+
#if os(Windows)
55+
import WinSDK
56+
#else
57+
import Darwin
58+
#endif
59+
60+
#if SWIFT_PACKAGE
61+
import Clang_C
62+
#endif
63+
64+
#if os(Windows)
65+
private let library = toolchainLoader.load(path: "libclang.dll")
66+
#else
67+
private let library = toolchainLoader.load(path: "libclang.dylib")
68+
#endif
69+
"""
70+
case .sourcekit:
71+
return """
72+
#if SWIFT_PACKAGE
73+
import SourceKit
74+
#endif
75+
76+
#if os(Linux)
77+
private let library = toolchainLoader.load(path: "libsourcekitdInProc.so")
78+
#elseif os(Windows)
79+
private let library = toolchainLoader.load(path: "sourcekitdInProc.dll")
80+
#else
81+
private let library = toolchainLoader.load(path: "sourcekitdInProc.framework/Versions/A/sourcekitdInProc")
82+
#endif
83+
"""
84+
}
85+
}
86+
87+
func extractFreeFunctions(compilerArguments: [String]) throws -> [String] {
88+
let sourceKitResponse = try interfaceForModule(moduleName, compilerArguments: compilerArguments)
89+
let substructure = SwiftDocKey.getSubstructure(Structure(sourceKitResponse: sourceKitResponse).dictionary)!
90+
let source = sourceKitResponse["key.sourcetext"] as! String
91+
return source.extractFreeFunctions(inSubstructure: substructure)
92+
}
93+
}
94+
95+
private func interfaceForModule(_ module: String, compilerArguments: [String]) throws -> [String: SourceKitRepresentable] {
96+
return try Request.customRequest(request: [
97+
"key.request": UID("source.request.editor.open.interface"),
98+
"key.name": UUID().uuidString,
99+
"key.compilerargs": compilerArguments,
100+
"key.modulename": module
101+
]).send()
102+
}
103+
104+
private extension String {
105+
func nameFromFullFunctionName() -> String {
106+
return String(self[..<range(of: "(")!.lowerBound])
107+
}
108+
109+
func extractFreeFunctions(inSubstructure substructure: [[String: SourceKitRepresentable]]) -> [String] {
110+
substructure
111+
.filter { SwiftDeclarationKind(rawValue: SwiftDocKey.getKind($0)!) == .functionFree }
112+
.compactMap { function -> String? in
113+
let name = (function["key.name"] as! String).nameFromFullFunctionName()
114+
let unsupportedFunctions = [
115+
"clang_executeOnThread",
116+
"sourcekitd_variant_dictionary_apply",
117+
"sourcekitd_variant_array_apply"
118+
]
119+
guard !unsupportedFunctions.contains(name) else {
120+
return nil
121+
}
122+
123+
let parameters = SwiftDocKey.getSubstructure(function)?.map { parameterStructure in
124+
return parameterStructure["key.typename"] as! String
125+
} ?? []
126+
var returnTypes = [String]()
127+
if let offset = SwiftDocKey.getOffset(function), let length = SwiftDocKey.getLength(function) {
128+
let stringView = StringView(self)
129+
if let functionDeclaration = stringView.substringWithByteRange(ByteRange(location: offset, length: length)),
130+
let startOfReturnArrow = functionDeclaration.range(of: "->", options: .backwards)?.lowerBound {
131+
let adjustedDistance = distance(from: startIndex, to: startOfReturnArrow)
132+
let adjustedReturnTypeStartIndex = functionDeclaration.index(functionDeclaration.startIndex,
133+
offsetBy: adjustedDistance + 3)
134+
returnTypes.append(String(functionDeclaration[adjustedReturnTypeStartIndex...]))
135+
}
136+
}
137+
138+
let joinedParameters = parameters.map({ $0.replacingOccurrences(of: "!", with: "?") }).joined(separator: ", ")
139+
let joinedReturnTypes = returnTypes.map({ $0.replacingOccurrences(of: "!", with: "?") }).joined(separator: ", ")
140+
let lhs = "internal let \(name): @convention(c) (\(joinedParameters)) -> (\(joinedReturnTypes))"
141+
let rhs = "library.load(symbol: \"\(name)\")"
142+
return "\(lhs) = \(rhs)".replacingOccurrences(of: "SourceKittenFramework.", with: "")
143+
}
144+
}
145+
}

Source/SourceKittenFramework/Request.swift

Lines changed: 0 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -416,91 +416,3 @@ extension Request: CustomStringConvertible {
416416
/// A textual representation of `Request`.
417417
public var description: String { return sourcekitObject.description }
418418
}
419-
420-
private func interfaceForModule(_ module: String, compilerArguments: [String]) throws -> [String: SourceKitRepresentable] {
421-
return try Request.customRequest(request: [
422-
"key.request": UID("source.request.editor.open.interface"),
423-
"key.name": NSUUID().uuidString,
424-
"key.compilerargs": compilerArguments,
425-
"key.modulename": module
426-
]).send()
427-
}
428-
429-
extension String {
430-
private func nameFromFullFunctionName() -> String {
431-
return String(self[..<range(of: "(")!.lowerBound])
432-
}
433-
434-
fileprivate func extractFreeFunctions(inSubstructure substructure: [[String: SourceKitRepresentable]]) -> [String] {
435-
return substructure.filter({
436-
SwiftDeclarationKind(rawValue: SwiftDocKey.getKind($0)!) == .functionFree
437-
}).compactMap { function -> String? in
438-
let name = (function["key.name"] as! String).nameFromFullFunctionName()
439-
let unsupportedFunctions = [
440-
"clang_executeOnThread",
441-
"sourcekitd_variant_dictionary_apply",
442-
"sourcekitd_variant_array_apply"
443-
]
444-
guard !unsupportedFunctions.contains(name) else {
445-
return nil
446-
}
447-
448-
let parameters = SwiftDocKey.getSubstructure(function)?.map { parameterStructure in
449-
return parameterStructure["key.typename"] as! String
450-
} ?? []
451-
var returnTypes = [String]()
452-
if let offset = SwiftDocKey.getOffset(function), let length = SwiftDocKey.getLength(function) {
453-
let stringView = StringView(self)
454-
if let functionDeclaration = stringView.substringWithByteRange(ByteRange(location: offset, length: length)),
455-
let startOfReturnArrow = functionDeclaration.range(of: "->", options: .backwards)?.lowerBound {
456-
let adjustedDistance = distance(from: startIndex, to: startOfReturnArrow)
457-
let adjustedReturnTypeStartIndex = functionDeclaration.index(functionDeclaration.startIndex,
458-
offsetBy: adjustedDistance + 3)
459-
returnTypes.append(String(functionDeclaration[adjustedReturnTypeStartIndex...]))
460-
}
461-
}
462-
463-
let joinedParameters = parameters.map({ $0.replacingOccurrences(of: "!", with: "?") }).joined(separator: ", ")
464-
let joinedReturnTypes = returnTypes.map({ $0.replacingOccurrences(of: "!", with: "?") }).joined(separator: ", ")
465-
let lhs = "internal let \(name): @convention(c) (\(joinedParameters)) -> (\(joinedReturnTypes))"
466-
let rhs = "library.load(symbol: \"\(name)\")"
467-
return "\(lhs) = \(rhs)".replacingOccurrences(of: "SourceKittenFramework.", with: "")
468-
}
469-
}
470-
}
471-
472-
internal func libraryWrapperForModule(_ module: String,
473-
macOSPath: String,
474-
linuxPath: String?,
475-
compilerArguments: [String]) throws -> String {
476-
let sourceKitResponse = try interfaceForModule(module, compilerArguments: compilerArguments)
477-
let substructure = SwiftDocKey.getSubstructure(Structure(sourceKitResponse: sourceKitResponse).dictionary)!
478-
let source = sourceKitResponse["key.sourcetext"] as! String
479-
let freeFunctions = source.extractFreeFunctions(inSubstructure: substructure)
480-
let spmImport = "#if SWIFT_PACKAGE\nimport \(module)\n#endif\n"
481-
let library: String
482-
if let linuxPath = linuxPath {
483-
library = """
484-
#if os(Linux)
485-
private let path = "\(linuxPath)"
486-
#else
487-
private let path = "\(macOSPath)"
488-
#endif
489-
private let library = toolchainLoader.load(path: path)
490-
491-
"""
492-
} else {
493-
library = "private let library = toolchainLoader.load(path: \"\(macOSPath)\")\n"
494-
}
495-
let swiftlintDisableComment = "// swiftlint:disable unused_declaration - We don't care if some of these are unused.\n"
496-
let startPlatformCheck: String
497-
let endPlatformCheck: String
498-
if linuxPath == nil {
499-
startPlatformCheck = "#if !os(Linux)\nimport Darwin\n"
500-
endPlatformCheck = "\n#endif\n"
501-
} else {
502-
startPlatformCheck = ""
503-
endPlatformCheck = "\n"
504-
}
505-
return startPlatformCheck + spmImport + library + swiftlintDisableComment + freeFunctions.joined(separator: "\n") + endPlatformCheck
506-
}

Source/SourceKittenFramework/SourceKitObject.swift

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,14 @@ import Foundation
33
import SourceKit
44
#endif
55

6+
#if os(Linux)
7+
import Glibc
8+
#elseif os(Windows)
9+
import ucrt
10+
#else
11+
import Darwin
12+
#endif
13+
614
// MARK: - SourceKitObjectConvertible
715

816
public protocol SourceKitObjectConvertible {
@@ -136,7 +144,7 @@ extension SourceKitObject: SourceKitObjectConvertible {
136144
extension SourceKitObject: CustomStringConvertible {
137145
public var description: String {
138146
let bytes = sourcekitd_request_description_copy(sourcekitdObject)!
139-
defer { bytes.deallocate() }
147+
defer { free(bytes) }
140148
return String(cString: bytes)
141149
}
142150
}

Source/SourceKittenFramework/SwiftDocs.swift

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
1+
import Foundation
2+
13
#if SWIFT_PACKAGE
24
import SourceKit
35
#endif
46

57
#if os(Linux)
68
import Glibc
9+
#elseif os(Windows)
10+
import CRT
711
#else
812
import Darwin
913
#endif
@@ -68,6 +72,14 @@ public struct SwiftDocs {
6872
extension SwiftDocs: CustomStringConvertible {
6973
/// A textual JSON representation of `SwiftDocs`.
7074
public var description: String {
71-
return toJSON(toNSDictionary([file.path ?? "<No File>": docsDictionary]))
75+
let source: String
76+
if let path = file.path {
77+
source = URL(fileURLWithPath: path).standardizedFileURL.withUnsafeFileSystemRepresentation {
78+
String(cString: $0!)
79+
}
80+
} else {
81+
source = "<No File>"
82+
}
83+
return toJSON(toNSDictionary([source: docsDictionary]))
7284
}
7385
}

Source/SourceKittenFramework/Xcode.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,8 @@ public func sdkPath() -> String {
189189
#if os(Linux)
190190
// xcrun does not exist on Linux
191191
return ""
192+
#elseif os(Windows)
193+
return ProcessInfo.processInfo.environment["SDKROOT"] ?? ""
192194
#else
193195
return Exec.run("/usr/bin/xcrun", "--show-sdk-path", "--sdk", "macosx").string ?? ""
194196
#endif

Source/SourceKittenFramework/library_wrapper_Clang_C.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,23 @@
11
#if !os(Linux)
2+
3+
#if os(Windows)
4+
import WinSDK
5+
#else
26
import Darwin
7+
#endif
8+
39
#if SWIFT_PACKAGE
410
import Clang_C
511
#endif
12+
13+
#if os(Windows)
14+
private let library = toolchainLoader.load(path: "libclang.dll")
15+
#else
616
private let library = toolchainLoader.load(path: "libclang.dylib")
17+
#endif
18+
719
// swiftlint:disable unused_declaration - We don't care if some of these are unused.
20+
821
internal let clang_getCString: @convention(c) (CXString) -> (UnsafePointer<CChar>?) = library.load(symbol: "clang_getCString")
922
internal let clang_disposeString: @convention(c) (CXString) -> () = library.load(symbol: "clang_disposeString")
1023
internal let clang_disposeStringSet: @convention(c) (UnsafeMutablePointer<CXStringSet>?) -> () = library.load(symbol: "clang_disposeStringSet")
@@ -386,4 +399,5 @@ internal let clang_VerbatimLineComment_getText: @convention(c) (CXComment) -> (C
386399
internal let clang_HTMLTagComment_getAsString: @convention(c) (CXComment) -> (CXString) = library.load(symbol: "clang_HTMLTagComment_getAsString")
387400
internal let clang_FullComment_getAsHTML: @convention(c) (CXComment) -> (CXString) = library.load(symbol: "clang_FullComment_getAsHTML")
388401
internal let clang_FullComment_getAsXML: @convention(c) (CXComment) -> (CXString) = library.load(symbol: "clang_FullComment_getAsXML")
402+
389403
#endif

Source/SourceKittenFramework/library_wrapper_SourceKit.swift

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
#if SWIFT_PACKAGE
22
import SourceKit
33
#endif
4+
45
#if os(Linux)
5-
private let path = "libsourcekitdInProc.so"
6+
private let library = toolchainLoader.load(path: "libsourcekitdInProc.so")
7+
#elseif os(Windows)
8+
private let library = toolchainLoader.load(path: "sourcekitdInProc.dll")
69
#else
7-
private let path = "sourcekitdInProc.framework/Versions/A/sourcekitdInProc"
10+
private let library = toolchainLoader.load(path: "sourcekitdInProc.framework/Versions/A/sourcekitdInProc")
811
#endif
9-
private let library = toolchainLoader.load(path: path)
12+
1013
// swiftlint:disable unused_declaration - We don't care if some of these are unused.
14+
1115
internal let sourcekitd_initialize: @convention(c) () -> () = library.load(symbol: "sourcekitd_initialize")
1216
internal let sourcekitd_shutdown: @convention(c) () -> () = library.load(symbol: "sourcekitd_shutdown")
1317
internal let sourcekitd_set_interrupted_connection_handler: @convention(c) (@escaping sourcekitd_interrupted_connection_handler_t) -> () = library.load(symbol: "sourcekitd_set_interrupted_connection_handler")

Source/sourcekitten/main.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import ArgumentParser
33
import Darwin
44
#elseif canImport(Glibc)
55
import Glibc
6+
#elseif os(Windows)
7+
import ucrt
68
#else
79
#error("Unsupported platform")
810
#endif

0 commit comments

Comments
 (0)