@@ -16,6 +16,8 @@ import LanguageServerProtocol
16
16
import SourceKitLSP
17
17
import TSCBasic
18
18
19
+ private struct SwiftSyntaxCShimsModulemapNotFoundError : Error { }
20
+
19
21
public class SwiftPMTestProject : MultiFileTestProject {
20
22
enum Error : Swift . Error {
21
23
/// The `swift` executable could not be found.
@@ -33,6 +35,114 @@ public class SwiftPMTestProject: MultiFileTestProject {
33
35
)
34
36
"""
35
37
38
+ /// A manifest that defines two targets:
39
+ /// - A macro target named `MyMacro`
40
+ /// - And executable target named `MyMacroClient`
41
+ ///
42
+ /// It builds the macro using the swift-syntax that was already built as part of the SourceKit-LSP build.
43
+ /// Re-using the SwiftSyntax modules that are already built is significantly faster than building swift-syntax in
44
+ /// each test case run and does not require internet access.
45
+ public static var macroPackageManifest : String {
46
+ get async throws {
47
+ // Directories that we should search for the swift-syntax package.
48
+ // We prefer a checkout in the build folder. If that doesn't exist, we are probably using local dependencies
49
+ // (SWIFTCI_USE_LOCAL_DEPS), so search next to the sourcekit-lsp source repo
50
+ let swiftSyntaxSearchPaths = [
51
+ productsDirectory
52
+ . deletingLastPathComponent ( ) // arm64-apple-macosx
53
+ . deletingLastPathComponent ( ) // debug
54
+ . appendingPathComponent ( " checkouts " ) ,
55
+ URL ( fileURLWithPath: #filePath)
56
+ . deletingLastPathComponent ( ) // SwiftPMTestProject.swift
57
+ . deletingLastPathComponent ( ) // SKTestSupport
58
+ . deletingLastPathComponent ( ) // Sources
59
+ . deletingLastPathComponent ( ) , // sourcekit-lsp
60
+ ]
61
+
62
+ let swiftSyntaxCShimsModulemap =
63
+ swiftSyntaxSearchPaths. map { swiftSyntaxSearchPath in
64
+ swiftSyntaxSearchPath
65
+ . appendingPathComponent ( " swift-syntax " )
66
+ . appendingPathComponent ( " Sources " )
67
+ . appendingPathComponent ( " _SwiftSyntaxCShims " )
68
+ . appendingPathComponent ( " include " )
69
+ . appendingPathComponent ( " module.modulemap " )
70
+ }
71
+ . first { FileManager . default. fileExists ( atPath: $0. path) }
72
+
73
+ guard let swiftSyntaxCShimsModulemap else {
74
+ throw SwiftSyntaxCShimsModulemapNotFoundError ( )
75
+ }
76
+
77
+ let swiftSyntaxModulesToLink = [
78
+ " SwiftBasicFormat " ,
79
+ " SwiftCompilerPlugin " ,
80
+ " SwiftCompilerPluginMessageHandling " ,
81
+ " SwiftDiagnostics " ,
82
+ " SwiftOperators " ,
83
+ " SwiftParser " ,
84
+ " SwiftParserDiagnostics " ,
85
+ " SwiftSyntax " ,
86
+ " SwiftSyntaxBuilder " ,
87
+ " SwiftSyntaxMacroExpansion " ,
88
+ " SwiftSyntaxMacros " ,
89
+ ]
90
+
91
+ var objectFiles : [ String ] = [ ]
92
+ for moduleName in swiftSyntaxModulesToLink {
93
+ let dir = productsDirectory. appendingPathComponent ( " \( moduleName) .build " )
94
+ let enumerator = FileManager . default. enumerator ( at: dir, includingPropertiesForKeys: nil )
95
+ while let file = enumerator? . nextObject ( ) as? URL {
96
+ if file. pathExtension == " o " {
97
+ objectFiles. append ( file. path)
98
+ }
99
+ }
100
+ }
101
+
102
+ let linkerFlags = objectFiles. map {
103
+ """
104
+ " -l " , " \( $0) " ,
105
+ """
106
+ } . joined ( separator: " \n " )
107
+
108
+ let moduleSearchPath : String
109
+ if let toolchainVersion = try await ToolchainRegistry . forTesting. default? . swiftVersion,
110
+ toolchainVersion < SwiftVersion ( 6 , 0 )
111
+ {
112
+ moduleSearchPath = productsDirectory. path
113
+ } else {
114
+ moduleSearchPath = " \( productsDirectory. path) /Modules "
115
+ }
116
+
117
+ return """
118
+ // swift-tools-version: 5.10
119
+
120
+ import PackageDescription
121
+ import CompilerPluginSupport
122
+
123
+ let package = Package(
124
+ name: " MyMacro " ,
125
+ platforms: [.macOS(.v10_15)],
126
+ targets: [
127
+ .macro(
128
+ name: " MyMacros " ,
129
+ swiftSettings: [.unsafeFlags([
130
+ " -I " , " \( moduleSearchPath) " ,
131
+ " -Xcc " , " -fmodule-map-file= \( swiftSyntaxCShimsModulemap. path) "
132
+ ])],
133
+ linkerSettings: [
134
+ .unsafeFlags([
135
+ \( linkerFlags)
136
+ ])
137
+ ]
138
+ ),
139
+ .executableTarget(name: " MyMacroClient " , dependencies: [ " MyMacros " ]),
140
+ ]
141
+ )
142
+ """
143
+ }
144
+ }
145
+
36
146
/// Create a new SwiftPM package with the given files.
37
147
///
38
148
/// If `index` is `true`, then the package will be built, indexing all modules within the package.
0 commit comments