@@ -222,6 +222,180 @@ fileprivate struct BuildOperationTests: CoreBasedTests {
222
222
}
223
223
}
224
224
225
+ @Test ( . requireSDKs( . host) , . requireThreadSafeWorkingDirectory)
226
+ func debuggableCommandLineTool( ) async throws {
227
+ try await withTemporaryDirectory { ( tmpDir: Path ) in
228
+ let testProject = try await TestProject (
229
+ " TestProject " ,
230
+ sourceRoot: tmpDir,
231
+ groupTree: TestGroup (
232
+ " SomeFiles " ,
233
+ children: [
234
+ TestFile ( " main.swift " ) ,
235
+ TestFile ( " dynamic.swift " ) ,
236
+ TestFile ( " static.swift " ) ,
237
+ ] ) ,
238
+ buildConfigurations: [
239
+ TestBuildConfiguration ( " Debug " , buildSettings: [
240
+ " ARCHS " : " $(ARCHS_STANDARD) " ,
241
+ " CODE_SIGNING_ALLOWED " : ProcessInfo . processInfo. hostOperatingSystem ( ) == . macOS ? " YES " : " NO " ,
242
+ " CODE_SIGN_IDENTITY " : " - " ,
243
+ " CODE_SIGN_ENTITLEMENTS " : " Entitlements.plist " ,
244
+ " DEFINES_MODULE " : " YES " ,
245
+ " PRODUCT_NAME " : " $(TARGET_NAME) " ,
246
+ " SDKROOT " : " $(HOST_PLATFORM) " ,
247
+ " SUPPORTED_PLATFORMS " : " $(HOST_PLATFORM) " ,
248
+ " SWIFT_VERSION " : swiftVersion,
249
+ " GCC_GENERATE_DEBUGGING_SYMBOLS " : " YES " ,
250
+ ] )
251
+ ] ,
252
+ targets: [
253
+ TestStandardTarget (
254
+ " tool " ,
255
+ type: . commandLineTool,
256
+ buildConfigurations: [
257
+ TestBuildConfiguration ( " Debug " , buildSettings: [
258
+ " LD_RUNPATH_SEARCH_PATHS " : " @loader_path/ " ,
259
+ ] )
260
+ ] ,
261
+ buildPhases: [
262
+ TestSourcesBuildPhase ( [ " main.swift " ] ) ,
263
+ TestFrameworksBuildPhase ( [
264
+ TestBuildFile ( . target( " dynamiclib " ) ) ,
265
+ TestBuildFile ( . target( " staticlib " ) ) ,
266
+ ] )
267
+ ] ,
268
+ dependencies: [
269
+ " dynamiclib " ,
270
+ " staticlib " ,
271
+ ]
272
+ ) ,
273
+ TestStandardTarget (
274
+ " dynamiclib " ,
275
+ type: . dynamicLibrary,
276
+ buildConfigurations: [
277
+ TestBuildConfiguration ( " Debug " , buildSettings: [
278
+ " DYLIB_INSTALL_NAME_BASE " : " $ORIGIN " ,
279
+ " DYLIB_INSTALL_NAME_BASE[sdk=macosx*] " : " @rpath " ,
280
+
281
+ // FIXME: Find a way to make these default
282
+ " EXECUTABLE_PREFIX " : " lib " ,
283
+ " EXECUTABLE_PREFIX[sdk=windows*] " : " " ,
284
+ ] )
285
+ ] ,
286
+ buildPhases: [
287
+ TestSourcesBuildPhase ( [ " dynamic.swift " ] ) ,
288
+ ]
289
+ ) ,
290
+ TestStandardTarget (
291
+ " staticlib " ,
292
+ type: . staticLibrary,
293
+ buildConfigurations: [
294
+ TestBuildConfiguration ( " Debug " , buildSettings: [
295
+ // FIXME: Find a way to make these default
296
+ " EXECUTABLE_PREFIX " : " lib " ,
297
+ " EXECUTABLE_PREFIX[sdk=windows*] " : " " ,
298
+ ] )
299
+ ] ,
300
+ buildPhases: [
301
+ TestSourcesBuildPhase ( [ " static.swift " ] ) ,
302
+ ]
303
+ ) ,
304
+ ] )
305
+ let core = try await getCore ( )
306
+ let tester = try await BuildOperationTester ( core, testProject, simulated: false )
307
+
308
+ let projectDir = tester. workspace. projects [ 0 ] . sourceRoot
309
+
310
+ try await tester. fs. writeFileContents ( projectDir. join ( " main.swift " ) ) { stream in
311
+ stream <<< " import dynamiclib \n "
312
+ stream <<< " import staticlib \n "
313
+ stream <<< " dynamicLib() \n "
314
+ stream <<< " dynamicLib() \n "
315
+ stream <<< " staticLib() \n "
316
+ stream <<< " print( \" Hello world \" ) \n "
317
+ }
318
+
319
+ try await tester. fs. writeFileContents ( projectDir. join ( " dynamic.swift " ) ) { stream in
320
+ stream <<< " public func dynamicLib() { } "
321
+ }
322
+
323
+ try await tester. fs. writeFileContents ( projectDir. join ( " static.swift " ) ) { stream in
324
+ stream <<< " public func staticLib() { } "
325
+ }
326
+
327
+ try await tester. fs. writePlist ( projectDir. join ( " Entitlements.plist " ) , . plDict( [ : ] ) )
328
+
329
+ let provisioningInputs = [
330
+ " dynamiclib " : ProvisioningTaskInputs ( identityHash: " - " , signedEntitlements: . plDict( [ : ] ) , simulatedEntitlements: . plDict( [ : ] ) ) ,
331
+ " staticlib " : ProvisioningTaskInputs ( identityHash: " - " , signedEntitlements: . plDict( [ : ] ) , simulatedEntitlements: . plDict( [ : ] ) ) ,
332
+ " tool " : ProvisioningTaskInputs ( identityHash: " - " , signedEntitlements: . plDict( [ : ] ) , simulatedEntitlements: . plDict( [ : ] ) )
333
+ ]
334
+
335
+ let destination : RunDestinationInfo = . host
336
+ try await tester. checkBuild ( runDestination: destination, persistent: true , signableTargets: Set ( provisioningInputs. keys) , signableTargetInputs: provisioningInputs) { results in
337
+ results. checkNoErrors ( )
338
+ if core. hostOperatingSystem. imageFormat. requiresSwiftModulewrap {
339
+ try results. checkTask ( . matchTargetName( " tool " ) , . matchRulePattern( [ " WriteAuxiliaryFile " , . suffix( " LinkFileList " ) ] ) ) { task in
340
+ let auxFileAction = try #require( task. action as? AuxiliaryFileTaskAction )
341
+ let contents = try tester. fs. read ( auxFileAction. context. input) . asString
342
+ let files = contents. components ( separatedBy: " \n " ) . map { $0. trimmingCharacters ( in: . whitespacesAndNewlines) } . filter { !$0. isEmpty }
343
+ #expect( files. count == 2 )
344
+ #expect( files [ 0 ] . hasSuffix ( " tool.o " ) )
345
+ #expect( files [ 1 ] . hasSuffix ( " main.o " ) )
346
+ }
347
+ let toolWrap = try #require( results. getTask ( . matchTargetName( " tool " ) , . matchRuleType( " SwiftModuleWrap " ) ) )
348
+ try results. checkTask ( . matchTargetName( " tool " ) , . matchRuleType( " Ld " ) ) { task in
349
+ try results. checkTaskFollows ( task, toolWrap)
350
+ }
351
+
352
+ try results. checkTask ( . matchTargetName( " dynamiclib " ) , . matchRulePattern( [ " WriteAuxiliaryFile " , . suffix( " LinkFileList " ) ] ) ) { task in
353
+ let auxFileAction = try #require( task. action as? AuxiliaryFileTaskAction )
354
+ let contents = try tester. fs. read ( auxFileAction. context. input) . asString
355
+ let files = contents. components ( separatedBy: " \n " ) . map { $0. trimmingCharacters ( in: . whitespacesAndNewlines) } . filter { !$0. isEmpty }
356
+ #expect( files. count == 2 )
357
+ #expect( files [ 0 ] . hasSuffix ( " dynamiclib.o " ) )
358
+ #expect( files [ 1 ] . hasSuffix ( " dynamic.o " ) )
359
+ }
360
+ let dylibWrap = try #require( results. getTask ( . matchTargetName( " dynamiclib " ) , . matchRuleType( " SwiftModuleWrap " ) ) )
361
+ try results. checkTask ( . matchTargetName( " dynamiclib " ) , . matchRuleType( " Ld " ) ) { task in
362
+ try results. checkTaskFollows ( task, dylibWrap)
363
+ }
364
+
365
+ try results. checkTask ( . matchTargetName( " staticlib " ) , . matchRulePattern( [ " WriteAuxiliaryFile " , . suffix( " LinkFileList " ) ] ) ) { task in
366
+ let auxFileAction = try #require( task. action as? AuxiliaryFileTaskAction )
367
+ let contents = try tester. fs. read ( auxFileAction. context. input) . asString
368
+ let files = contents. components ( separatedBy: " \n " ) . map { $0. trimmingCharacters ( in: . whitespacesAndNewlines) } . filter { !$0. isEmpty }
369
+ #expect( files. count == 2 )
370
+ #expect( files [ 0 ] . hasSuffix ( " staticlib.o " ) )
371
+ #expect( files [ 1 ] . hasSuffix ( " static.o " ) )
372
+ }
373
+ let staticWrap = try #require( results. getTask ( . matchTargetName( " staticlib " ) , . matchRuleType( " SwiftModuleWrap " ) ) )
374
+ try results. checkTask ( . matchTargetName( " staticlib " ) , . matchRuleType( " Libtool " ) ) { task in
375
+ try results. checkTaskFollows ( task, staticWrap)
376
+ }
377
+ }
378
+
379
+ let toolchain = try #require( try await getCore ( ) . toolchainRegistry. defaultToolchain)
380
+ let environment : [ String : String ]
381
+ if destination. platform == " linux " {
382
+ environment = [ " LD_LIBRARY_PATH " : toolchain. path. join ( " usr/lib/swift/linux " ) . str]
383
+ } else {
384
+ environment = [ : ]
385
+ }
386
+
387
+ let executionResult = try await Process . getOutput ( url: URL ( fileURLWithPath: projectDir. join ( " build " ) . join ( " Debug \( destination. builtProductsDirSuffix) " ) . join ( core. hostOperatingSystem. imageFormat. executableName ( basename: " tool " ) ) . str) , arguments: [ ] , environment: environment)
388
+ #expect( executionResult. exitStatus == . exit( 0 ) )
389
+ if core. hostOperatingSystem == . windows {
390
+ #expect( String ( decoding: executionResult. stdout, as: UTF8 . self) == " Hello world \r \n " )
391
+ } else {
392
+ #expect( String ( decoding: executionResult. stdout, as: UTF8 . self) == " Hello world \n " )
393
+ }
394
+ #expect( String ( decoding: executionResult. stderr, as: UTF8 . self) == " " )
395
+ }
396
+ }
397
+ }
398
+
225
399
/// Check that environment variables are propagated from the user environment correctly.
226
400
@Test ( . requireSDKs( . host) , . skipHostOS( . windows) , . requireSystemPackages( apt: " yacc " , yum: " byacc " ) )
227
401
func userEnvironment( ) async throws {
0 commit comments