12
12
13
13
import PackagePlugin
14
14
import Foundation
15
+ #if os(Linux)
16
+ import Glibc
17
+ #endif
15
18
16
19
@main
17
20
struct CMakeSmokeTest : CommandPlugin {
@@ -24,21 +27,24 @@ struct CMakeSmokeTest: CommandPlugin {
24
27
}
25
28
26
29
guard let cmakePath = args. extractOption ( named: " cmake-path " ) . last else { throw Errors . missingRequiredOption ( " --cmake-path " ) }
27
- print ( " using cmake at \( cmakePath) " )
30
+ Diagnostics . progress ( " using cmake at \( cmakePath) " )
28
31
let cmakeURL = URL ( filePath: cmakePath)
29
32
guard let ninjaPath = args. extractOption ( named: " ninja-path " ) . last else { throw Errors . missingRequiredOption ( " --ninja-path " ) }
30
- print ( " using ninja at \( ninjaPath) " )
33
+ Diagnostics . progress ( " using ninja at \( ninjaPath) " )
31
34
let ninjaURL = URL ( filePath: ninjaPath)
32
35
let sysrootPath = args. extractOption ( named: " sysroot-path " ) . last
33
36
if let sysrootPath {
34
- print ( " using sysroot at \( sysrootPath) " )
37
+ Diagnostics . progress ( " using sysroot at \( sysrootPath) " )
35
38
}
36
39
40
+ let extraCMakeArgs = args. extractOption ( named: " extra-cmake-arg " )
41
+ Diagnostics . progress ( " Extra cmake args: \( extraCMakeArgs. joined ( separator: " " ) ) " )
42
+
37
43
let moduleCachePath = context. pluginWorkDirectoryURL. appending ( component: " module-cache " ) . path ( )
38
44
39
45
let swiftBuildURL = context. package . directoryURL
40
46
let swiftBuildBuildURL = context. pluginWorkDirectoryURL. appending ( component: " swift-build " )
41
- print ( " swift-build: \( swiftBuildURL. path ( ) ) " )
47
+ Diagnostics . progress ( " swift-build: \( swiftBuildURL. path ( ) ) " )
42
48
43
49
let swiftToolsSupportCoreURL = try findDependency ( " swift-tools-support-core " , pluginContext: context)
44
50
let swiftToolsSupportCoreBuildURL = context. pluginWorkDirectoryURL. appending ( component: " swift-tools-support-core " )
@@ -80,39 +86,39 @@ struct CMakeSmokeTest: CommandPlugin {
80
86
" -DCMAKE_MAKE_PROGRAM= \( ninjaPath) " ,
81
87
" -DCMAKE_BUILD_TYPE:=Debug " ,
82
88
" -DCMAKE_Swift_FLAGS=' \( sharedSwiftFlags. joined ( separator: " " ) ) ' "
83
- ] + cMakeProjectArgs
89
+ ] + cMakeProjectArgs + extraCMakeArgs
84
90
85
- print ( " Building swift-tools-support-core " )
91
+ Diagnostics . progress ( " Building swift-tools-support-core " )
86
92
try await Process . checkNonZeroExit ( url: cmakeURL, arguments: sharedCMakeArgs + [ swiftToolsSupportCoreURL. path ( ) ] , workingDirectory: swiftToolsSupportCoreBuildURL)
87
93
try await Process . checkNonZeroExit ( url: ninjaURL, arguments: [ ] , workingDirectory: swiftToolsSupportCoreBuildURL)
88
- print ( " Built swift-tools-support-core " )
94
+ Diagnostics . progress ( " Built swift-tools-support-core " )
89
95
90
96
if hostOS != . macOS {
91
- print ( " Building swift-system " )
97
+ Diagnostics . progress ( " Building swift-system " )
92
98
try await Process . checkNonZeroExit ( url: cmakeURL, arguments: sharedCMakeArgs + [ swiftSystemURL. path ( ) ] , workingDirectory: swiftSystemBuildURL)
93
99
try await Process . checkNonZeroExit ( url: ninjaURL, arguments: [ ] , workingDirectory: swiftSystemBuildURL)
94
- print ( " Built swift-system " )
100
+ Diagnostics . progress ( " Built swift-system " )
95
101
}
96
102
97
- print ( " Building llbuild " )
103
+ Diagnostics . progress ( " Building llbuild " )
98
104
try await Process . checkNonZeroExit ( url: cmakeURL, arguments: sharedCMakeArgs + [ " -DLLBUILD_SUPPORT_BINDINGS:=Swift " , llbuildURL. path ( ) ] , workingDirectory: llbuildBuildURL)
99
105
try await Process . checkNonZeroExit ( url: ninjaURL, arguments: [ ] , workingDirectory: llbuildBuildURL)
100
- print ( " Built llbuild " )
106
+ Diagnostics . progress ( " Built llbuild " )
101
107
102
- print ( " Building swift-argument-parser " )
108
+ Diagnostics . progress ( " Building swift-argument-parser " )
103
109
try await Process . checkNonZeroExit ( url: cmakeURL, arguments: sharedCMakeArgs + [ " -DBUILD_TESTING=NO " , " -DBUILD_EXAMPLES=NO " , swiftArgumentParserURL. path ( ) ] , workingDirectory: swiftArgumentParserBuildURL)
104
110
try await Process . checkNonZeroExit ( url: ninjaURL, arguments: [ ] , workingDirectory: swiftArgumentParserBuildURL)
105
- print ( " Built swift-argument-parser " )
111
+ Diagnostics . progress ( " Built swift-argument-parser " )
106
112
107
- print ( " Building swift-driver " )
113
+ Diagnostics . progress ( " Building swift-driver " )
108
114
try await Process . checkNonZeroExit ( url: cmakeURL, arguments: sharedCMakeArgs + [ swiftDriverURL. path ( ) ] , workingDirectory: swiftDriverBuildURL)
109
115
try await Process . checkNonZeroExit ( url: ninjaURL, arguments: [ ] , workingDirectory: swiftDriverBuildURL)
110
- print ( " Built swift-driver " )
116
+ Diagnostics . progress ( " Built swift-driver " )
111
117
112
- print ( " Building swift-build in \( swiftBuildBuildURL) " )
118
+ Diagnostics . progress ( " Building swift-build in \( swiftBuildBuildURL) " )
113
119
try await Process . checkNonZeroExit ( url: cmakeURL, arguments: sharedCMakeArgs + [ swiftBuildURL. path ( ) ] , workingDirectory: swiftBuildBuildURL)
114
120
try await Process . checkNonZeroExit ( url: ninjaURL, arguments: [ ] , workingDirectory: swiftBuildBuildURL)
115
- print ( " Built swift-build " )
121
+ Diagnostics . progress ( " Built swift-build " )
116
122
}
117
123
118
124
func findDependency( _ name: String , pluginContext: PluginContext ) throws -> URL {
@@ -132,7 +138,7 @@ struct CMakeSmokeTest: CommandPlugin {
132
138
throw Errors . missingRepository ( name)
133
139
}
134
140
let dependencyURL = dependency. directoryURL
135
- print ( " \( name) : \( dependencyURL. path ( ) ) " )
141
+ Diagnostics . progress ( " \( name) : \( dependencyURL. path ( ) ) " )
136
142
guard FileManager . default. fileExists ( atPath: dependencyURL. path ( ) ) else {
137
143
throw Errors . missingRepository ( dependencyURL. path ( ) )
138
144
}
@@ -145,6 +151,7 @@ enum Errors: Error {
145
151
case missingRequiredOption( String )
146
152
case missingRepository( String )
147
153
case unimplementedForHostOS
154
+ case miscError( String )
148
155
}
149
156
150
157
enum OS {
@@ -182,7 +189,52 @@ extension Process {
182
189
}
183
190
184
191
static func checkNonZeroExit( url: URL , arguments: [ String ] , workingDirectory: URL , environment: [ String : String ] ? = nil ) async throws {
185
- print ( " \( url. path ( ) ) \( arguments. joined ( separator: " " ) ) " )
192
+ Diagnostics . progress ( " \( url. path ( ) ) \( arguments. joined ( separator: " " ) ) " )
193
+ #if os(Linux)
194
+ // Linux workaround for https://github.com/swiftlang/swift-corelibs-foundation/issues/4772
195
+ // Foundation.Process on Linux seems to inherit the Process.run()-calling thread's signal mask, creating processes that even have SIGTERM blocked
196
+ // This manifests as CMake hanging when invoking 'uname' with incorrectly configured signal handlers.
197
+ var fileActions = posix_spawn_file_actions_t ( )
198
+ defer { posix_spawn_file_actions_destroy ( & fileActions) }
199
+ var attrs : posix_spawnattr_t = posix_spawnattr_t ( )
200
+ defer { posix_spawnattr_destroy ( & attrs) }
201
+ posix_spawn_file_actions_init ( & fileActions)
202
+ posix_spawn_file_actions_addchdir_np ( & fileActions, workingDirectory. path ( ) )
203
+
204
+ posix_spawnattr_init ( & attrs)
205
+ posix_spawnattr_setpgroup ( & attrs, 0 )
206
+ var noSignals = sigset_t ( )
207
+ sigemptyset ( & noSignals)
208
+ posix_spawnattr_setsigmask ( & attrs, & noSignals)
209
+
210
+ var mostSignals = sigset_t ( )
211
+ sigemptyset ( & mostSignals)
212
+ for i in 1 ..< SIGSYS {
213
+ if i == SIGKILL || i == SIGSTOP {
214
+ continue
215
+ }
216
+ sigaddset ( & mostSignals, i)
217
+ }
218
+ posix_spawnattr_setsigdefault ( & attrs, & mostSignals)
219
+ posix_spawnattr_setflags ( & attrs, numericCast ( POSIX_SPAWN_SETPGROUP | POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK) )
220
+ var pid : pid_t = - 1
221
+ try withArrayOfCStrings ( [ url. path ( ) ] + arguments) { arguments in
222
+ try withArrayOfCStrings ( ( environment ?? [ : ] ) . map { key, value in " \( key) = \( value) " } ) { environment in
223
+ let spawnResult = posix_spawn ( & pid, url. path ( ) , /*file_actions=*/& fileActions, /*attrp=*/& attrs, arguments, nil ) ;
224
+ var exitCode : Int32 = - 1
225
+ var result = wait4 ( pid, & exitCode, 0 , nil ) ;
226
+ while ( result == - 1 && errno == EINTR) {
227
+ result = wait4 ( pid, & exitCode, 0 , nil )
228
+ }
229
+ guard result != - 1 else {
230
+ throw Errors . miscError ( " wait failed " )
231
+ }
232
+ guard exitCode == 0 else {
233
+ throw Errors . miscError ( " exit code nonzero " )
234
+ }
235
+ }
236
+ }
237
+ #else
186
238
let process = Process ( )
187
239
process. executableURL = url
188
240
process. arguments = arguments
@@ -192,5 +244,44 @@ extension Process {
192
244
if process. terminationStatus != 0 {
193
245
throw Errors . processError ( terminationReason: process. terminationReason, terminationStatus: process. terminationStatus)
194
246
}
247
+ #endif
248
+ }
249
+ }
250
+
251
+ func scan< S: Sequence , U> ( _ seq: S , _ initial: U , _ combine: ( U , S . Element ) -> U ) -> [ U ] {
252
+ var result : [ U ] = [ ]
253
+ result. reserveCapacity ( seq. underestimatedCount)
254
+ var runningResult = initial
255
+ for element in seq {
256
+ runningResult = combine ( runningResult, element)
257
+ result. append ( runningResult)
258
+ }
259
+ return result
260
+ }
261
+
262
+ func withArrayOfCStrings< T> (
263
+ _ args: [ String ] ,
264
+ _ body: ( UnsafePointer < UnsafeMutablePointer < Int8 > ? > ) throws -> T
265
+ ) throws -> T {
266
+ let argsCounts = Array ( args. map { $0. utf8. count + 1 } )
267
+ let argsOffsets = [ 0 ] + scan( argsCounts, 0 , + )
268
+ let argsBufferSize = argsOffsets. last!
269
+ var argsBuffer : [ UInt8 ] = [ ]
270
+ argsBuffer. reserveCapacity ( argsBufferSize)
271
+ for arg in args {
272
+ argsBuffer. append ( contentsOf: arg. utf8)
273
+ argsBuffer. append ( 0 )
274
+ }
275
+ return try argsBuffer. withUnsafeMutableBufferPointer {
276
+ ( argsBuffer) in
277
+ let ptr = UnsafeRawPointer ( argsBuffer. baseAddress!) . bindMemory (
278
+ to: Int8 . self, capacity: argsBuffer. count)
279
+ var cStrings : [ UnsafePointer < Int8 > ? ] = argsOffsets. map { ptr + $0 }
280
+ cStrings [ cStrings. count - 1 ] = nil
281
+ return try cStrings. withUnsafeMutableBufferPointer {
282
+ let unsafeString = UnsafeMutableRawPointer ( $0. baseAddress!) . bindMemory (
283
+ to: UnsafeMutablePointer< Int8>? . self , capacity: $0. count)
284
+ return try body ( unsafeString)
195
285
}
286
+ }
196
287
}
0 commit comments