@@ -1133,323 +1133,6 @@ extension SwiftPackageTool {
1133
1133
}
1134
1134
}
1135
1135
1136
- final class PluginDelegate : PluginInvocationDelegate {
1137
- let swiftTool : SwiftTool
1138
- let plugin : PluginTarget
1139
- var lineBufferedOutput : Data
1140
-
1141
- init ( swiftTool: SwiftTool , plugin: PluginTarget ) {
1142
- self . swiftTool = swiftTool
1143
- self . plugin = plugin
1144
- self . lineBufferedOutput = Data ( )
1145
- }
1146
-
1147
- func pluginCompilationStarted( commandLine: [ String ] , environment: EnvironmentVariables ) {
1148
- }
1149
-
1150
- func pluginCompilationEnded( result: PluginCompilationResult ) {
1151
- }
1152
-
1153
- func pluginCompilationWasSkipped( cachedResult: PluginCompilationResult ) {
1154
- }
1155
-
1156
- func pluginEmittedOutput( _ data: Data ) {
1157
- lineBufferedOutput += data
1158
- while let newlineIdx = lineBufferedOutput. firstIndex ( of: UInt8 ( ascii: " \n " ) ) {
1159
- let lineData = lineBufferedOutput. prefix ( upTo: newlineIdx)
1160
- print ( String ( decoding: lineData, as: UTF8 . self) )
1161
- lineBufferedOutput = lineBufferedOutput. suffix ( from: newlineIdx. advanced ( by: 1 ) )
1162
- }
1163
- }
1164
-
1165
- func pluginEmittedDiagnostic( _ diagnostic: Basics . Diagnostic ) {
1166
- swiftTool. observabilityScope. emit ( diagnostic)
1167
- }
1168
-
1169
- func pluginRequestedBuildOperation( subset: PluginInvocationBuildSubset , parameters: PluginInvocationBuildParameters , completion: @escaping ( Result < PluginInvocationBuildResult , Error > ) -> Void ) {
1170
- // Run the build in the background and call the completion handler when done.
1171
- DispatchQueue . sharedConcurrent. async {
1172
- completion ( Result {
1173
- return try self . performBuildForPlugin ( subset: subset, parameters: parameters)
1174
- } )
1175
- }
1176
- }
1177
-
1178
- private func performBuildForPlugin( subset: PluginInvocationBuildSubset , parameters: PluginInvocationBuildParameters ) throws -> PluginInvocationBuildResult {
1179
- // Configure the build parameters.
1180
- var buildParameters = try self . swiftTool. buildParameters ( )
1181
- switch parameters. configuration {
1182
- case . debug:
1183
- buildParameters. configuration = . debug
1184
- case . release:
1185
- buildParameters. configuration = . release
1186
- }
1187
- buildParameters. flags. cCompilerFlags. append ( contentsOf: parameters. otherCFlags)
1188
- buildParameters. flags. cxxCompilerFlags. append ( contentsOf: parameters. otherCxxFlags)
1189
- buildParameters. flags. swiftCompilerFlags. append ( contentsOf: parameters. otherSwiftcFlags)
1190
- buildParameters. flags. linkerFlags. append ( contentsOf: parameters. otherLinkerFlags)
1191
-
1192
- // Configure the verbosity of the output.
1193
- let logLevel : Diagnostic . Severity
1194
- switch parameters. logging {
1195
- case . concise:
1196
- logLevel = . warning
1197
- case . verbose:
1198
- logLevel = . info
1199
- case . debug:
1200
- logLevel = . debug
1201
- }
1202
-
1203
- // Determine the subset of products and targets to build.
1204
- var explicitProduct : String ? = . none
1205
- let buildSubset : BuildSubset
1206
- switch subset {
1207
- case . all( let includingTests) :
1208
- buildSubset = includingTests ? . allIncludingTests : . allExcludingTests
1209
- case . product( let name) :
1210
- buildSubset = . product( name)
1211
- explicitProduct = name
1212
- case . target( let name) :
1213
- buildSubset = . target( name)
1214
- }
1215
-
1216
- // Create a build operation. We have to disable the cache in order to get a build plan created.
1217
- let outputStream = BufferedOutputByteStream ( )
1218
- let buildSystem = try swiftTool. createBuildSystem (
1219
- explicitBuildSystem: . native,
1220
- explicitProduct: explicitProduct,
1221
- cacheBuildManifest: false ,
1222
- customBuildParameters: buildParameters,
1223
- customOutputStream: outputStream,
1224
- customLogLevel: logLevel
1225
- )
1226
-
1227
- // Run the build. This doesn't return until the build is complete.
1228
- let success = buildSystem. buildIgnoringError ( subset: buildSubset)
1229
-
1230
- // Create and return the build result record based on what the delegate collected and what's in the build plan.
1231
- let builtProducts = try buildSystem. buildPlan. buildProducts. filter {
1232
- switch subset {
1233
- case . all( let includingTests) :
1234
- return includingTests ? true : $0. product. type != . test
1235
- case . product( let name) :
1236
- return $0. product. name == name
1237
- case . target( let name) :
1238
- return $0. product. name == name
1239
- }
1240
- }
1241
- let builtArtifacts : [ PluginInvocationBuildResult . BuiltArtifact ] = builtProducts. compactMap {
1242
- switch $0. product. type {
1243
- case . library( let kind) :
1244
- return . init( path: $0. binaryPath. pathString, kind: ( kind == . dynamic) ? . dynamicLibrary : . staticLibrary)
1245
- case . executable:
1246
- return . init( path: $0. binaryPath. pathString, kind: . executable)
1247
- default :
1248
- return nil
1249
- }
1250
- }
1251
- return PluginInvocationBuildResult (
1252
- succeeded: success,
1253
- logText: outputStream. bytes. cString,
1254
- builtArtifacts: builtArtifacts)
1255
- }
1256
-
1257
- func pluginRequestedTestOperation( subset: PluginInvocationTestSubset , parameters: PluginInvocationTestParameters , completion: @escaping ( Result < PluginInvocationTestResult , Error > ) -> Void ) {
1258
- // Run the test in the background and call the completion handler when done.
1259
- DispatchQueue . sharedConcurrent. async {
1260
- completion ( Result {
1261
- return try self . performTestsForPlugin ( subset: subset, parameters: parameters)
1262
- } )
1263
- }
1264
- }
1265
-
1266
- func performTestsForPlugin( subset: PluginInvocationTestSubset , parameters: PluginInvocationTestParameters ) throws -> PluginInvocationTestResult {
1267
- // Build the tests. Ideally we should only build those that match the subset, but we don't have a way to know which ones they are until we've built them and can examine the binaries.
1268
- let toolchain = try swiftTool. getDestinationToolchain ( )
1269
- var buildParameters = try swiftTool. buildParameters ( )
1270
- buildParameters. enableTestability = true
1271
- buildParameters. enableCodeCoverage = parameters. enableCodeCoverage
1272
- let buildSystem = try swiftTool. createBuildSystem ( customBuildParameters: buildParameters)
1273
- try buildSystem. build ( subset: . allIncludingTests)
1274
-
1275
- // Clean out the code coverage directory that may contain stale `profraw` files from a previous run of the code coverage tool.
1276
- if parameters. enableCodeCoverage {
1277
- try swiftTool. fileSystem. removeFileTree ( buildParameters. codeCovPath)
1278
- }
1279
-
1280
- // Construct the environment we'll pass down to the tests.
1281
- let testEnvironment = try TestingSupport . constructTestEnvironment (
1282
- toolchain: toolchain,
1283
- buildParameters: buildParameters,
1284
- sanitizers: swiftTool. options. build. sanitizers
1285
- )
1286
-
1287
- // Iterate over the tests and run those that match the filter.
1288
- var testTargetResults : [ PluginInvocationTestResult . TestTarget ] = [ ]
1289
- var numFailedTests = 0
1290
- for testProduct in buildSystem. builtTestProducts {
1291
- // Get the test suites in the bundle. Each is just a container for test cases.
1292
- let testSuites = try TestingSupport . getTestSuites (
1293
- fromTestAt: testProduct. bundlePath,
1294
- swiftTool: swiftTool,
1295
- enableCodeCoverage: parameters. enableCodeCoverage,
1296
- sanitizers: swiftTool. options. build. sanitizers
1297
- )
1298
- for testSuite in testSuites {
1299
- // Each test suite is just a container for test cases (confusingly called "tests", though they are test cases).
1300
- for testCase in testSuite. tests {
1301
- // Each test case corresponds to a combination of target and a XCTestCase, and is a collection of tests that can actually be run.
1302
- var testResults : [ PluginInvocationTestResult . TestTarget . TestCase . Test ] = [ ]
1303
- for testName in testCase. tests {
1304
- // Check if we should filter out this test.
1305
- let testSpecifier = testCase. name + " / " + testName
1306
- if case . filtered( let regexes) = subset {
1307
- guard regexes. contains ( where: { testSpecifier. range ( of: $0, options: . regularExpression) != nil } ) else {
1308
- continue
1309
- }
1310
- }
1311
-
1312
- // Configure a test runner.
1313
- let testRunner = TestRunner (
1314
- bundlePaths: [ testProduct. bundlePath] ,
1315
- xctestArg: testSpecifier,
1316
- cancellator: swiftTool. cancellator,
1317
- toolchain: toolchain,
1318
- testEnv: testEnvironment,
1319
- observabilityScope: swiftTool. observabilityScope)
1320
-
1321
- // Run the test — for now we run the sequentially so we can capture accurate timing results.
1322
- let startTime = DispatchTime . now ( )
1323
- let success = testRunner. test ( outputHandler: { _ in } ) // this drops the tests output
1324
- let duration = Double ( startTime. distance ( to: . now( ) ) . milliseconds ( ) ?? 0 ) / 1000.0
1325
- numFailedTests += success ? 0 : 1
1326
- testResults. append ( . init( name: testName, result: success ? . succeeded : . failed, duration: duration) )
1327
- }
1328
-
1329
- // Don't add any results if we didn't run any tests.
1330
- if testResults. isEmpty { continue }
1331
-
1332
- // Otherwise we either create a new create a new target result or add to the previous one, depending on whether the target name is the same.
1333
- let testTargetName = testCase. name. prefix ( while: { $0 != " . " } )
1334
- if let lastTestTargetName = testTargetResults. last? . name, testTargetName == lastTestTargetName {
1335
- // Same as last one, just extend its list of cases. We know we have a last one at this point.
1336
- testTargetResults [ testTargetResults. count- 1 ] . testCases. append ( . init( name: testCase. name, tests: testResults) )
1337
- }
1338
- else {
1339
- // Not the same, so start a new target result.
1340
- testTargetResults. append ( . init( name: String ( testTargetName) , testCases: [ . init( name: testCase. name, tests: testResults) ] ) )
1341
- }
1342
- }
1343
- }
1344
- }
1345
-
1346
- // Deal with code coverage, if enabled.
1347
- let codeCoverageDataFile : AbsolutePath ?
1348
- if parameters. enableCodeCoverage {
1349
- // Use `llvm-prof` to merge all the `.profraw` files into a single `.profdata` file.
1350
- let mergedCovFile = buildParameters. codeCovDataFile
1351
- let codeCovFileNames = try swiftTool. fileSystem. getDirectoryContents ( buildParameters. codeCovPath)
1352
- var llvmProfCommand = [ try toolchain. getLLVMProf ( ) . pathString]
1353
- llvmProfCommand += [ " merge " , " -sparse " ]
1354
- for fileName in codeCovFileNames where fileName. hasSuffix ( " .profraw " ) {
1355
- let filePath = buildParameters. codeCovPath. appending ( component: fileName)
1356
- llvmProfCommand. append ( filePath. pathString)
1357
- }
1358
- llvmProfCommand += [ " -o " , mergedCovFile. pathString]
1359
- try TSCBasic . Process. checkNonZeroExit ( arguments: llvmProfCommand)
1360
-
1361
- // Use `llvm-cov` to export the merged `.profdata` file contents in JSON form.
1362
- var llvmCovCommand = [ try toolchain. getLLVMCov ( ) . pathString]
1363
- llvmCovCommand += [ " export " , " -instr-profile= \( mergedCovFile. pathString) " ]
1364
- for product in buildSystem. builtTestProducts {
1365
- llvmCovCommand. append ( " -object " )
1366
- llvmCovCommand. append ( product. binaryPath. pathString)
1367
- }
1368
- // We get the output on stdout, and have to write it to a JSON ourselves.
1369
- let jsonOutput = try TSCBasic . Process. checkNonZeroExit ( arguments: llvmCovCommand)
1370
- let jsonCovFile = buildParameters. codeCovDataFile. parentDirectory. appending ( component: buildParameters. codeCovDataFile. basenameWithoutExt + " .json " )
1371
- try swiftTool. fileSystem. writeFileContents ( jsonCovFile, string: jsonOutput)
1372
-
1373
- // Return the path of the exported code coverage data file.
1374
- codeCoverageDataFile = jsonCovFile
1375
- }
1376
- else {
1377
- codeCoverageDataFile = nil
1378
- }
1379
-
1380
- // Return the results to the plugin. We only consider the test run a success if no test failed.
1381
- return PluginInvocationTestResult (
1382
- succeeded: ( numFailedTests == 0 ) ,
1383
- testTargets: testTargetResults,
1384
- codeCoverageDataFile: codeCoverageDataFile? . pathString)
1385
- }
1386
-
1387
- func pluginRequestedSymbolGraph( forTarget targetName: String , options: PluginInvocationSymbolGraphOptions , completion: @escaping ( Result < PluginInvocationSymbolGraphResult , Error > ) -> Void ) {
1388
- // Extract the symbol graph in the background and call the completion handler when done.
1389
- DispatchQueue . sharedConcurrent. async {
1390
- completion ( Result {
1391
- return try self . createSymbolGraphForPlugin ( forTarget: targetName, options: options)
1392
- } )
1393
- }
1394
- }
1395
-
1396
- private func createSymbolGraphForPlugin( forTarget targetName: String , options: PluginInvocationSymbolGraphOptions ) throws -> PluginInvocationSymbolGraphResult {
1397
- // Current implementation uses `SymbolGraphExtract()` but in the future we should emit the symbol graph while building.
1398
-
1399
- // Create a build system for building the target., skipping the the cache because we need the build plan.
1400
- let buildSystem = try swiftTool. createBuildSystem ( explicitBuildSystem: . native, cacheBuildManifest: false )
1401
-
1402
- // Find the target in the build operation's package graph; it's an error if we don't find it.
1403
- let packageGraph = try buildSystem. getPackageGraph ( )
1404
- guard let target = packageGraph. allTargets. first ( where: { $0. name == targetName } ) else {
1405
- throw StringError ( " could not find a target named “ \( targetName) ” " )
1406
- }
1407
-
1408
- // Build the target, if needed.
1409
- try buildSystem. build ( subset: . target( target. name) )
1410
-
1411
- // Configure the symbol graph extractor.
1412
- var symbolGraphExtractor = try SymbolGraphExtract (
1413
- fileSystem: swiftTool. fileSystem,
1414
- tool: swiftTool. getDestinationToolchain ( ) . getSymbolGraphExtract ( )
1415
- )
1416
- symbolGraphExtractor. skipSynthesizedMembers = !options. includeSynthesized
1417
- switch options. minimumAccessLevel {
1418
- case . private:
1419
- symbolGraphExtractor. minimumAccessLevel = . private
1420
- case . fileprivate:
1421
- symbolGraphExtractor. minimumAccessLevel = . fileprivate
1422
- case . internal:
1423
- symbolGraphExtractor. minimumAccessLevel = . internal
1424
- case . public:
1425
- symbolGraphExtractor. minimumAccessLevel = . public
1426
- case . open:
1427
- symbolGraphExtractor. minimumAccessLevel = . open
1428
- }
1429
- symbolGraphExtractor. skipInheritedDocs = true
1430
- symbolGraphExtractor. includeSPISymbols = options. includeSPI
1431
-
1432
- // Determine the output directory, and remove any old version if it already exists.
1433
- guard let package = packageGraph. package ( for: target) else {
1434
- throw StringError ( " could not determine the package for target “ \( target. name) ” " )
1435
- }
1436
- let outputDir = try buildSystem. buildPlan. buildParameters. dataPath. appending ( components: " extracted-symbols " , package . identity. description, target. name)
1437
- try swiftTool. fileSystem. removeFileTree ( outputDir)
1438
-
1439
- // Run the symbol graph extractor on the target.
1440
- try symbolGraphExtractor. extractSymbolGraph (
1441
- target: target,
1442
- buildPlan: try buildSystem. buildPlan,
1443
- outputRedirection: . collect,
1444
- outputDirectory: outputDir,
1445
- verboseOutput: self . swiftTool. logLevel <= . info
1446
- )
1447
-
1448
- // Return the results to the plugin.
1449
- return PluginInvocationSymbolGraphResult ( directoryPath: outputDir. pathString)
1450
- }
1451
- }
1452
-
1453
1136
extension PluginCommandIntent {
1454
1137
var invocationVerb : String {
1455
1138
switch self {
@@ -1882,14 +1565,3 @@ private extension Basics.Diagnostic {
1882
1565
. error( " missing required argument \( argument) " )
1883
1566
}
1884
1567
}
1885
-
1886
- extension BuildSystem {
1887
- fileprivate func buildIgnoringError( subset: BuildSubset ) -> Bool {
1888
- do {
1889
- try self . build ( subset: subset)
1890
- return true
1891
- } catch {
1892
- return false
1893
- }
1894
- }
1895
- }
0 commit comments