@@ -29,24 +29,34 @@ public struct Module {
2929 /**
3030 Failable initializer to create a Module from a Swift Package Manager build record.
3131
32- Use this initializer when the package has already been built and the `.build` directory exists.
32+ Use this initializer when the package has already been built and the `.build` directory exists
33+ and the build system is not Swift Build.
34+
35+ This initializer does not work with the Swift Build backend that is the default in Swift PM 6.4.
36+ Use `init(spmArguments:spmName:inPath:)` instead, which will build the package.
3337
3438 - parameter spmName: Module name. Will use some non-Test module that is part of the
3539 package if `nil`.
3640 - parameter path: Path of the directory containing the SPM `.build` directory.
3741 Uses the current directory by default.
3842 */
43+ @available ( * , deprecated, message: """
44+ Use init(spmArguments:spmName:inPath:) instead.
45+ This initializer does not support the Swift Build backend that is the default for SwiftPM 6.4
46+ """ )
3947 public init ? ( spmName: String ? = nil , inPath path: String = FileManager . default. currentDirectoryPath) {
4048 guard let results = SwiftPM . fromDebugYaml ( moduleName: spmName, inPath: path) else {
4149 return nil
4250 }
51+ fputs ( " Using module data from debug.yaml \n " , stderr)
4352 self . init ( name: results. 0 , compilerArguments: results. 1 )
4453 }
4554
4655 /**
4756 Failable initializer to create a Module by building a Swift Package Manager project.
4857
49- Use this initializer if the package has not been built or may have changed since last built.
58+ As of SwiftPM 6.4 this is likely to build the package even it has been previously built because
59+ of the build system changing to Swift Build.
5060
5161 - parameter spmArguments: Additional arguments to pass to `swift build`
5262 - parameter spmName: Module name. Will use some non-Test module that is part of the
@@ -55,12 +65,64 @@ public struct Module {
5565 Uses the current directory by default.
5666 */
5767 public init ? ( spmArguments: [ String ] , spmName: String ? = nil , inPath path: String = FileManager . default. currentDirectoryPath) {
68+ /*
69+ 1. `build -v` to get a build directory and maybe some compile commands for (3).
70+ Fast if already built.
71+ 2. If there is a `debug.yaml` file then use it -- pre-6.4 / swiftbuild back-end. Done.
72+ 3. Check for compiler flags in the build log from (1) -- if successful then Done.
73+ 4. `package clean` and `build -v` --- hope that (3) failed because module already built
74+ before (1) so it did not rebuild anything.
75+ 5. Check for compiler flags in the build log from (4) -- if successful then Done.
76+ 6. Fail.
77+
78+ The swiftbuild backend uses a proprietary binary-ish msgpack format instead of the debug.yaml.
79+ We could decode it and save the extra clean-build-verbose step but would mean heavier
80+ dependencies and more fragility.
81+
82+ The swiftbuild backend produces an XCBuildData/manifest.json that looks promising but does not
83+ contain compiler arguments.
84+ */
85+
86+ // 1. Initial build check
5887 fputs ( " Running swift build \n " , stderr)
59- guard SwiftPM . runBuild ( arguments: spmArguments, inPath: path) != nil else {
88+ guard let buildResults = SwiftPM . runVerboseBuild ( arguments: spmArguments, inPath: path) else {
89+ return nil
90+ }
91+
92+ // 2. Pre-Swift Build solution
93+ if SwiftPM . hasDebugYaml ( inPath: path) {
94+ if let info = SwiftPM . fromDebugYaml ( moduleName: spmName, inPath: path) {
95+ fputs ( " Using module data from debug.yaml \n " , stderr)
96+ self . init ( name: info. 0 , compilerArguments: info. 1 )
97+ return
98+ }
99+ return nil
100+ }
101+
102+ // 3. See if the (1) build built our module
103+ if let info = SwiftPM . fromBuildResults ( buildResults, moduleName: spmName) {
104+ fputs ( " Using module data from build output \n " , stderr)
105+ self . init ( name: info. 0 , compilerArguments: info. 1 )
106+ return
107+ }
108+
109+ // 4. Clean & Build
110+ fputs ( " Running swift package clean and build \n " , stderr)
111+ guard SwiftPM . runClean ( inPath: path) != nil ,
112+ let secondBuildResults = SwiftPM . runVerboseBuild ( arguments: spmArguments, inPath: path) else {
60113 return nil
61114 }
62115
63- self . init ( spmName: spmName, inPath: path)
116+ // 5. Should have our module now
117+ if let info = SwiftPM . fromBuildResults ( secondBuildResults, moduleName: spmName) {
118+ fputs ( " Using module data from clean build output \n " , stderr)
119+ self . init ( name: info. 0 , compilerArguments: info. 1 )
120+ return
121+ }
122+
123+ let path = secondBuildResults. save ( prefix: " swift-build " )
124+ fputs ( " Could not parse module name ' \( spmName ?? " (any) " ) ' from swift build output: \( path) \n " , stderr)
125+ return nil
64126 }
65127
66128 /**
0 commit comments