From 1cc83c8a42c4f04a80248b360766ff122e02183c Mon Sep 17 00:00:00 2001 From: Ian Leitch Date: Sun, 15 Sep 2024 18:54:00 +0100 Subject: [PATCH] Remove singleton patterns --- .mise/tasks/lint | 2 +- .../Commands/CheckUpdateCommand.swift | 5 +- .../Frontend/Commands/ClearCacheCommand.swift | 5 +- Sources/Frontend/Commands/ScanBehavior.swift | 20 +++--- Sources/Frontend/Commands/ScanCommand.swift | 13 +++- Sources/Frontend/CommonSetupGuide.swift | 2 +- Sources/Frontend/GuidedSetup.swift | 16 +++-- Sources/Frontend/Project.swift | 60 ++++++++++++---- Sources/Frontend/Scan.swift | 16 +++-- Sources/Frontend/UpdateChecker.swift | 2 +- Sources/Frontend/main.swift | 5 +- Sources/Indexer/IndexPipeline.swift | 35 ++++++++-- Sources/Indexer/InfoPlistIndexer.swift | 4 +- Sources/Indexer/SourceFileCollector.swift | 2 +- Sources/Indexer/SwiftIndexer.swift | 2 +- Sources/Indexer/XCDataModelIndexer.swift | 4 +- Sources/Indexer/XCMappingModelIndexer.swift | 4 +- Sources/Indexer/XibIndexer.swift | 4 +- .../Results/OutputDeclarationFilter.swift | 2 +- .../ProjectDrivers/BazelProjectDriver.swift | 11 ++- .../ProjectDrivers/GenericProjectDriver.swift | 25 +++---- Sources/ProjectDrivers/SPMProjectDriver.swift | 19 +++-- .../ProjectDrivers/XcodeProjectDriver.swift | 45 ++++++++---- Sources/Shared/Configuration.swift | 10 +-- Sources/Shared/Extensions/FilePath+Glob.swift | 29 +++----- Sources/Shared/Logger.swift | 69 ++++++------------- Sources/Shared/SPM.swift | 23 ++++--- Sources/Shared/Shell.swift | 6 +- Sources/Shared/SwiftVersion.swift | 8 +-- .../Mutators/AccessibilityCascader.swift | 2 +- .../AncestralReferenceEliminator.swift | 2 +- .../Mutators/AssetReferenceRetainer.swift | 2 +- ...ssignOnlyPropertyReferenceEliminator.swift | 2 +- .../CapitalSelfFunctionCallRetainer.swift | 6 +- .../Mutators/CodablePropertyRetainer.swift | 2 +- .../CodingKeyEnumReferenceBuilder.swift | 2 +- ...plexPropertyAccessorReferenceBuilder.swift | 2 +- .../DefaultConstructorReferenceBuilder.swift | 2 +- .../DynamicMemberLookupReferenceBuilder.swift | 2 +- .../EntryPointAttributeRetainer.swift | 2 +- .../Mutators/EnumCaseReferenceBuilder.swift | 2 +- .../Mutators/ExtensionReferenceBuilder.swift | 2 +- ...eProtocolConformanceReferenceRemover.swift | 2 +- ...AndStructConstructorReferenceBuilder.swift | 2 +- .../Mutators/ObjCAccessibleRetainer.swift | 2 +- .../Mutators/PropertyWrapperRetainer.swift | 2 +- .../ProtocolConformanceReferenceBuilder.swift | 2 +- .../ProtocolExtensionReferenceBuilder.swift | 2 +- .../Mutators/PubliclyAccessibleRetainer.swift | 2 +- ...antExplicitPublicAccessibilityMarker.swift | 2 +- .../Mutators/RedundantProtocolMarker.swift | 2 +- .../Mutators/ResultBuilderRetainer.swift | 2 +- ...rpolationAppendInterpolationRetainer.swift | 2 +- ...tImplicitInitializerReferenceBuilder.swift | 2 +- .../Mutators/SwiftUIRetainer.swift | 2 +- .../Mutators/UnusedImportMarker.swift | 2 +- .../Mutators/UnusedParameterRetainer.swift | 2 +- .../Mutators/UsedDeclarationMarker.swift | 2 +- .../SourceGraph/Mutators/XCTestRetainer.swift | 2 +- Sources/SourceGraph/SourceGraph.swift | 5 +- Sources/SourceGraph/SourceGraphMutator.swift | 2 +- .../SourceGraphMutatorRunner.swift | 12 ++-- Sources/XcodeSupport/XcodeProject.swift | 32 ++++----- .../XcodeSupport/XcodeProjectSetupGuide.swift | 45 ++++++++---- Sources/XcodeSupport/XcodeWorkspace.swift | 4 +- Sources/XcodeSupport/Xcodebuild.swift | 2 +- Tests/PeripheryTests/RetentionTest.swift | 1 - Tests/PeripheryTests/ShellTest.swift | 4 +- Tests/Shared/SPMSourceGraphTestCase.swift | 4 +- Tests/Shared/SourceGraphTestCase.swift | 21 ++++-- Tests/XcodeTests/ShellMock.swift | 4 +- .../XcodeTests/XcodeSourceGraphTestCase.swift | 9 ++- Tests/XcodeTests/XcodeTargetTest.swift | 24 ++++++- .../XcodebuildBuildProjectTest.swift | 7 +- Tests/XcodeTests/XcodebuildSchemesTest.swift | 7 +- 75 files changed, 401 insertions(+), 294 deletions(-) diff --git a/.mise/tasks/lint b/.mise/tasks/lint index a9d02dfc1..6b47c9fe7 100755 --- a/.mise/tasks/lint +++ b/.mise/tasks/lint @@ -4,5 +4,5 @@ set -euo pipefail cd $MISE_PROJECT_ROOT -swiftformat --quiet --strict +swiftformat --quiet --strict . swiftlint lint --quiet --strict diff --git a/Sources/Frontend/Commands/CheckUpdateCommand.swift b/Sources/Frontend/Commands/CheckUpdateCommand.swift index 6c222645e..cf1329552 100644 --- a/Sources/Frontend/Commands/CheckUpdateCommand.swift +++ b/Sources/Frontend/Commands/CheckUpdateCommand.swift @@ -9,8 +9,9 @@ struct CheckUpdateCommand: FrontendCommand { ) func run() throws { - let logger = Logger() - let checker = UpdateChecker() + let configuration = Configuration() + let logger = Logger(configuration: configuration) + let checker = UpdateChecker(logger: logger, configuration: configuration) DispatchQueue.global().async { checker.run() } let latestVersion = try checker.wait().get() if latestVersion.isVersion(greaterThan: PeripheryVersion) { diff --git a/Sources/Frontend/Commands/ClearCacheCommand.swift b/Sources/Frontend/Commands/ClearCacheCommand.swift index 1309171a7..9897fdda4 100644 --- a/Sources/Frontend/Commands/ClearCacheCommand.swift +++ b/Sources/Frontend/Commands/ClearCacheCommand.swift @@ -9,6 +9,9 @@ struct ClearCacheCommand: FrontendCommand { ) func run() throws { - try Shell.shared.exec(["rm", "-rf", Constants.cachePath().string]) + let configuration = Configuration() + let logger = Logger(configuration: configuration) + let shell = Shell(logger: logger) + try shell.exec(["rm", "-rf", Constants.cachePath().string]) } } diff --git a/Sources/Frontend/Commands/ScanBehavior.swift b/Sources/Frontend/Commands/ScanBehavior.swift index aeb35809b..9befb17f8 100644 --- a/Sources/Frontend/Commands/ScanBehavior.swift +++ b/Sources/Frontend/Commands/ScanBehavior.swift @@ -6,15 +6,19 @@ import SystemPackage final class ScanBehavior { private let configuration: Configuration private let logger: Logger + private let shell: Shell + private let swiftVersion: SwiftVersion - required init(configuration: Configuration = .shared, logger: Logger = .init()) { + required init(configuration: Configuration, logger: Logger, shell: Shell, swiftVersion: SwiftVersion) { self.configuration = configuration self.logger = logger + self.shell = shell + self.swiftVersion = swiftVersion } func setup(_ configPath: FilePath?) -> Result { do { - try configuration.load(from: configPath) + try configuration.load(from: configPath, logger: logger) } catch let error as PeripheryError { return .failure(error) } catch { @@ -30,13 +34,13 @@ final class ScanBehavior { let project: Project do { - logger.debug(SwiftVersion.current.fullVersion) - try SwiftVersion.current.validateVersion() + logger.debug(swiftVersion.fullVersion) + try swiftVersion.validateVersion() if configuration.guidedSetup { - project = try GuidedSetup().perform() + project = try GuidedSetup(configuration: configuration, shell: shell, logger: logger).perform() } else { - project = try Project.identify() + project = try Project(configuration: configuration, shell: shell, logger: logger) } } catch let error as PeripheryError { return .failure(error) @@ -44,7 +48,7 @@ final class ScanBehavior { return .failure(.underlyingError(error)) } - let updateChecker = UpdateChecker() + let updateChecker = UpdateChecker(logger: logger, configuration: configuration) updateChecker.run() let results: [ScanResult] @@ -60,7 +64,7 @@ final class ScanBehavior { baseline = try JSONDecoder().decode(Baseline.self, from: data) } - let filteredResults = try OutputDeclarationFilter().filter(results, with: baseline) + let filteredResults = try OutputDeclarationFilter(configuration: configuration, logger: logger).filter(results, with: baseline) if let baselinePath = configuration.writeBaseline { let usrs = filteredResults diff --git a/Sources/Frontend/Commands/ScanCommand.swift b/Sources/Frontend/Commands/ScanCommand.swift index cd0bda347..cee0171ba 100644 --- a/Sources/Frontend/Commands/ScanCommand.swift +++ b/Sources/Frontend/Commands/ScanCommand.swift @@ -135,13 +135,16 @@ struct ScanCommand: FrontendCommand { private static let defaultConfiguration = Configuration() func run() throws { - let scanBehavior = ScanBehavior() + let configuration = Configuration() + let logger = Logger(configuration: configuration) + let shell = Shell(logger: logger) + let swiftVersion = SwiftVersion(shell: shell) + let scanBehavior = ScanBehavior(configuration: configuration, logger: logger, shell: shell, swiftVersion: swiftVersion) if !setup { try scanBehavior.setup(config).get() } - let configuration = Configuration.shared configuration.guidedSetup = setup configuration.apply(\.$project, project) configuration.apply(\.$schemes, schemes) @@ -184,7 +187,11 @@ struct ScanCommand: FrontendCommand { configuration.apply(\.$bazelFilter, bazelFilter) try scanBehavior.main { project in - try Scan().perform(project: project) + try Scan( + configuration: configuration, + logger: logger, + swiftVersion: swiftVersion + ).perform(project: project) }.get() } } diff --git a/Sources/Frontend/CommonSetupGuide.swift b/Sources/Frontend/CommonSetupGuide.swift index 421bc471c..8d8aa14fb 100644 --- a/Sources/Frontend/CommonSetupGuide.swift +++ b/Sources/Frontend/CommonSetupGuide.swift @@ -4,7 +4,7 @@ import Shared final class CommonSetupGuide: SetupGuideHelpers { private let configuration: Configuration - required init(configuration: Configuration = .shared) { + required init(configuration: Configuration) { self.configuration = configuration super.init() } diff --git a/Sources/Frontend/GuidedSetup.swift b/Sources/Frontend/GuidedSetup.swift index 7d618b4a3..edce4b8e5 100644 --- a/Sources/Frontend/GuidedSetup.swift +++ b/Sources/Frontend/GuidedSetup.swift @@ -6,12 +6,16 @@ import Shared #endif final class GuidedSetup: SetupGuideHelpers { - required init(configuration: Configuration = .shared) { + private let configuration: Configuration + private let shell: Shell + private let logger: Logger + + required init(configuration: Configuration, shell: Shell, logger: Logger) { self.configuration = configuration + self.shell = shell + self.logger = logger } - private let configuration: Configuration - func perform() throws -> Project { print(colorize("Welcome to Periphery!", .boldGreen)) print("This guided setup will help you select the appropriate configuration for your project.\n") @@ -23,7 +27,7 @@ final class GuidedSetup: SetupGuideHelpers { } #if canImport(XcodeSupport) - if let guide = XcodeProjectSetupGuide.detect() { + if let guide = XcodeProjectSetupGuide(configuration: configuration, shell: shell, logger: logger) { projectGuides.append(guide) } #endif @@ -46,9 +50,9 @@ final class GuidedSetup: SetupGuideHelpers { print(colorize("*", .boldGreen) + " Inspecting project...") let kind = try projectGuide.perform() - let project = Project(kind: kind) + let project = Project(kind: kind, configuration: configuration, shell: shell, logger: logger) - let commonGuide = CommonSetupGuide() + let commonGuide = CommonSetupGuide(configuration: configuration) try commonGuide.perform() let options = projectGuide.commandLineOptions + commonGuide.commandLineOptions diff --git a/Sources/Frontend/Project.swift b/Sources/Frontend/Project.swift index 71df29625..ca1924635 100644 --- a/Sources/Frontend/Project.swift +++ b/Sources/Frontend/Project.swift @@ -4,42 +4,74 @@ import Shared import SystemPackage final class Project { - static func identify() throws -> Self { - let configuration = Configuration.shared + let kind: ProjectKind + + private let configuration: Configuration + private let shell: Shell + private let logger: Logger + + convenience init( + configuration: Configuration, + shell: Shell, + logger: Logger + ) throws { + var kind: ProjectKind? if let path = configuration.project { - return self.init(kind: .xcode(projectPath: path)) + kind = .xcode(projectPath: path) } else if let path = configuration.genericProjectConfig { - return self.init(kind: .generic(genericProjectConfig: path)) + kind = .generic(genericProjectConfig: path) } else if BazelProjectDriver.isSupported, configuration.bazel { - return self.init(kind: .bazel) + kind = .bazel } else if SPM.isSupported { - return self.init(kind: .spm) + kind = .spm } - throw PeripheryError.usageError("Failed to identify project in the current directory. For Xcode projects use the '--project' option, and for SPM projects change to the directory containing the Package.swift.") - } + guard let kind else { + throw PeripheryError.usageError("Failed to identify project in the current directory. For Xcode projects use the '--project' option, and for SPM projects change to the directory containing the Package.swift.") + } - let kind: ProjectKind + self.init(kind: kind, configuration: configuration, shell: shell, logger: logger) + } - init(kind: ProjectKind) { + init( + kind: ProjectKind, + configuration: Configuration, + shell: Shell, + logger: Logger + ) { self.kind = kind + self.configuration = configuration + self.shell = shell + self.logger = logger } func driver() throws -> ProjectDriver { switch kind { case let .xcode(projectPath): #if canImport(XcodeSupport) - return try XcodeProjectDriver.build(projectPath: projectPath) + return try XcodeProjectDriver( + projectPath: projectPath, + configuration: configuration, + shell: shell, + logger: logger + ) #else fatalError("Xcode projects are not supported on this platform.") #endif case .spm: - return try SPMProjectDriver.build() + return try SPMProjectDriver(configuration: configuration, shell: shell, logger: logger) case .bazel: - return try BazelProjectDriver.build() + return try BazelProjectDriver.build( + configuration: configuration, + shell: shell, + logger: logger + ) case let .generic(genericProjectConfig): - return try GenericProjectDriver.build(genericProjectConfig: genericProjectConfig) + return try GenericProjectDriver( + genericProjectConfig: genericProjectConfig, + configuration: configuration + ) } } } diff --git a/Sources/Frontend/Scan.swift b/Sources/Frontend/Scan.swift index 376105d07..2a40a52b6 100644 --- a/Sources/Frontend/Scan.swift +++ b/Sources/Frontend/Scan.swift @@ -8,11 +8,14 @@ import SourceGraph final class Scan { private let configuration: Configuration private let logger: Logger - private let graph = SourceGraph.shared + private let graph: SourceGraph + private let swiftVersion: SwiftVersion - required init(configuration: Configuration = .shared, logger: Logger = .init()) { + required init(configuration: Configuration, logger: Logger, swiftVersion: SwiftVersion) { self.configuration = configuration self.logger = logger + self.swiftVersion = swiftVersion + graph = SourceGraph(configuration: configuration) } func perform(project: Project) throws -> [ScanResult] { @@ -71,7 +74,7 @@ final class Scan { let indexLogger = logger.contextualized(with: "index") let plan = try driver.plan(logger: indexLogger) let syncSourceGraph = SynchronizedSourceGraph(graph: graph) - let pipeline = IndexPipeline(plan: plan, graph: syncSourceGraph, logger: indexLogger) + let pipeline = IndexPipeline(plan: plan, graph: syncSourceGraph, logger: indexLogger, configuration: configuration) try pipeline.perform() logger.endInterval(indexInterval) } @@ -84,7 +87,12 @@ final class Scan { logger.info("\(asterisk) Analyzing...") } - try SourceGraphMutatorRunner.perform(graph: graph) + try SourceGraphMutatorRunner( + graph: graph, + logger: logger, + configuration: configuration, + swiftVersion: swiftVersion + ).perform() logger.endInterval(analyzeInterval) } diff --git a/Sources/Frontend/UpdateChecker.swift b/Sources/Frontend/UpdateChecker.swift index 4e7de130d..a8629d61f 100644 --- a/Sources/Frontend/UpdateChecker.swift +++ b/Sources/Frontend/UpdateChecker.swift @@ -15,7 +15,7 @@ final class UpdateChecker { private let semaphore: DispatchSemaphore private var error: Error? - required init(logger: Logger = .init(), configuration: Configuration = .shared) { + required init(logger: Logger, configuration: Configuration) { self.logger = logger debugLogger = logger.contextualized(with: "update-check") self.configuration = configuration diff --git a/Sources/Frontend/main.swift b/Sources/Frontend/main.swift index 9e7ed7f38..793f32931 100644 --- a/Sources/Frontend/main.swift +++ b/Sources/Frontend/main.swift @@ -17,12 +17,13 @@ struct PeripheryCommand: FrontendCommand { } signal(SIGINT) { _ in - let logger = Logger() + let logger = Logger(configuration: Configuration()) logger.warn( "Termination can result in a corrupt index. Try the '--clean-build' flag if you get erroneous results such as false-positives and incorrect source file locations.", newlinePrefix: true // Print a newline after ^C ) - Shell.shared.interruptRunning() + let shell = Shell(logger: logger) + shell.interruptRunning() exit(0) } diff --git a/Sources/Indexer/IndexPipeline.swift b/Sources/Indexer/IndexPipeline.swift index 06fcec182..c47d0313e 100644 --- a/Sources/Indexer/IndexPipeline.swift +++ b/Sources/Indexer/IndexPipeline.swift @@ -6,34 +6,57 @@ public struct IndexPipeline { private let plan: IndexPlan private let graph: SynchronizedSourceGraph private let logger: ContextualLogger + private let configuration: Configuration - public init(plan: IndexPlan, graph: SynchronizedSourceGraph, logger: ContextualLogger) { + public init(plan: IndexPlan, graph: SynchronizedSourceGraph, logger: ContextualLogger, configuration: Configuration) { self.plan = plan self.graph = graph self.logger = logger + self.configuration = configuration } public func perform() throws { try SwiftIndexer( sourceFiles: plan.sourceFiles, graph: graph, - logger: logger + logger: logger, + configuration: configuration ).perform() if !plan.plistPaths.isEmpty { - try InfoPlistIndexer(infoPlistFiles: plan.plistPaths, graph: graph).perform() + try InfoPlistIndexer( + infoPlistFiles: plan.plistPaths, + graph: graph, + logger: logger, + configuration: configuration + ).perform() } if !plan.xibPaths.isEmpty { - try XibIndexer(xibFiles: plan.xibPaths, graph: graph).perform() + try XibIndexer( + xibFiles: plan.xibPaths, + graph: graph, + logger: logger, + configuration: configuration + ).perform() } if !plan.xcDataModelPaths.isEmpty { - try XCDataModelIndexer(files: plan.xcDataModelPaths, graph: graph).perform() + try XCDataModelIndexer( + files: plan.xcDataModelPaths, + graph: graph, + logger: logger, + configuration: configuration + ).perform() } if !plan.xcMappingModelPaths.isEmpty { - try XCMappingModelIndexer(files: plan.xcMappingModelPaths, graph: graph).perform() + try XCMappingModelIndexer( + files: plan.xcMappingModelPaths, + graph: graph, + logger: logger, + configuration: configuration + ).perform() } graph.indexingComplete() diff --git a/Sources/Indexer/InfoPlistIndexer.swift b/Sources/Indexer/InfoPlistIndexer.swift index b00dc4870..0ebeffe23 100644 --- a/Sources/Indexer/InfoPlistIndexer.swift +++ b/Sources/Indexer/InfoPlistIndexer.swift @@ -7,10 +7,10 @@ final class InfoPlistIndexer: Indexer { private let graph: SynchronizedSourceGraph private let logger: ContextualLogger - required init(infoPlistFiles: Set, graph: SynchronizedSourceGraph, logger: Logger = .init(), configuration: Configuration = .shared) { + required init(infoPlistFiles: Set, graph: SynchronizedSourceGraph, logger: ContextualLogger, configuration: Configuration) { self.infoPlistFiles = infoPlistFiles self.graph = graph - self.logger = logger.contextualized(with: "index:infoplist") + self.logger = logger.contextualized(with: "infoplist") super.init(configuration: configuration) } diff --git a/Sources/Indexer/SourceFileCollector.swift b/Sources/Indexer/SourceFileCollector.swift index 4370f0de1..85c008f3d 100644 --- a/Sources/Indexer/SourceFileCollector.swift +++ b/Sources/Indexer/SourceFileCollector.swift @@ -14,7 +14,7 @@ public struct SourceFileCollector { indexStorePaths: Set, excludedTestTargets: Set, logger: ContextualLogger, - configuration: Configuration = .shared + configuration: Configuration ) { self.indexStorePaths = indexStorePaths self.excludedTestTargets = excludedTestTargets diff --git a/Sources/Indexer/SwiftIndexer.swift b/Sources/Indexer/SwiftIndexer.swift index 3b065c5c4..b40dd8aa9 100644 --- a/Sources/Indexer/SwiftIndexer.swift +++ b/Sources/Indexer/SwiftIndexer.swift @@ -20,7 +20,7 @@ final class SwiftIndexer: Indexer { sourceFiles: [SourceFile: [IndexUnit]], graph: SynchronizedSourceGraph, logger: ContextualLogger, - configuration: Configuration = .shared + configuration: Configuration ) { self.sourceFiles = sourceFiles self.graph = graph diff --git a/Sources/Indexer/XCDataModelIndexer.swift b/Sources/Indexer/XCDataModelIndexer.swift index 12e25ecbc..25b4c46c7 100644 --- a/Sources/Indexer/XCDataModelIndexer.swift +++ b/Sources/Indexer/XCDataModelIndexer.swift @@ -7,10 +7,10 @@ final class XCDataModelIndexer: Indexer { private let graph: SynchronizedSourceGraph private let logger: ContextualLogger - required init(files: Set, graph: SynchronizedSourceGraph, logger: Logger = .init(), configuration: Configuration = .shared) { + required init(files: Set, graph: SynchronizedSourceGraph, logger: ContextualLogger, configuration: Configuration) { self.files = files self.graph = graph - self.logger = logger.contextualized(with: "index:xcdatamodel") + self.logger = logger.contextualized(with: "xcdatamodel") super.init(configuration: configuration) } diff --git a/Sources/Indexer/XCMappingModelIndexer.swift b/Sources/Indexer/XCMappingModelIndexer.swift index 363caacb3..2439e33a6 100644 --- a/Sources/Indexer/XCMappingModelIndexer.swift +++ b/Sources/Indexer/XCMappingModelIndexer.swift @@ -7,10 +7,10 @@ final class XCMappingModelIndexer: Indexer { private let graph: SynchronizedSourceGraph private let logger: ContextualLogger - required init(files: Set, graph: SynchronizedSourceGraph, logger: Logger = .init(), configuration: Configuration = .shared) { + required init(files: Set, graph: SynchronizedSourceGraph, logger: ContextualLogger, configuration: Configuration) { self.files = files self.graph = graph - self.logger = logger.contextualized(with: "index:xcmappingmodel") + self.logger = logger.contextualized(with: "xcmappingmodel") super.init(configuration: configuration) } diff --git a/Sources/Indexer/XibIndexer.swift b/Sources/Indexer/XibIndexer.swift index f2bc29a64..5d7b34e70 100644 --- a/Sources/Indexer/XibIndexer.swift +++ b/Sources/Indexer/XibIndexer.swift @@ -7,10 +7,10 @@ final class XibIndexer: Indexer { private let graph: SynchronizedSourceGraph private let logger: ContextualLogger - required init(xibFiles: Set, graph: SynchronizedSourceGraph, logger: Logger = .init(), configuration: Configuration = .shared) { + required init(xibFiles: Set, graph: SynchronizedSourceGraph, logger: ContextualLogger, configuration: Configuration) { self.xibFiles = xibFiles self.graph = graph - self.logger = logger.contextualized(with: "index:xib") + self.logger = logger.contextualized(with: "xib") super.init(configuration: configuration) } diff --git a/Sources/PeripheryKit/Results/OutputDeclarationFilter.swift b/Sources/PeripheryKit/Results/OutputDeclarationFilter.swift index 89379f492..bce83a782 100644 --- a/Sources/PeripheryKit/Results/OutputDeclarationFilter.swift +++ b/Sources/PeripheryKit/Results/OutputDeclarationFilter.swift @@ -8,7 +8,7 @@ public final class OutputDeclarationFilter { private let logger: Logger private let contextualLogger: ContextualLogger - public required init(configuration: Configuration = .shared, logger: Logger = .init()) { + public required init(configuration: Configuration, logger: Logger) { self.configuration = configuration self.logger = logger contextualLogger = logger.contextualized(with: "report:filter") diff --git a/Sources/ProjectDrivers/BazelProjectDriver.swift b/Sources/ProjectDrivers/BazelProjectDriver.swift index f9e751bde..5d212a66c 100644 --- a/Sources/ProjectDrivers/BazelProjectDriver.swift +++ b/Sources/ProjectDrivers/BazelProjectDriver.swift @@ -7,11 +7,10 @@ public class BazelProjectDriver: ProjectDriver { FilePath("MODULE.bazel").exists || FilePath("WORKSPACE").exists } - public static func build() throws -> Self { - let configuration = Configuration.shared + public static func build(configuration: Configuration, shell: Shell, logger: Logger) throws -> Self { configuration.bazel = false // Generic project mode is used for the actual scan. configuration.reportExclude.append("**/bazel-out/**/*") - return self.init(configuration: configuration) + return self.init(configuration: configuration, shell: shell, logger: logger) } private static let topLevelKinds = [ @@ -69,9 +68,9 @@ public class BazelProjectDriver: ProjectDriver { private lazy var contextLogger: ContextualLogger = logger.contextualized(with: "bazel") required init( - configuration: Configuration = .shared, - shell: Shell = .shared, - logger: Logger = .init(), + configuration: Configuration, + shell: Shell, + logger: Logger, fileManager: FileManager = .default ) { self.configuration = configuration diff --git a/Sources/ProjectDrivers/GenericProjectDriver.swift b/Sources/ProjectDrivers/GenericProjectDriver.swift index 52ddab1d7..4b2f1a883 100644 --- a/Sources/ProjectDrivers/GenericProjectDriver.swift +++ b/Sources/ProjectDrivers/GenericProjectDriver.swift @@ -14,7 +14,15 @@ public final class GenericProjectDriver { let testTargets: Set } - public static func build(genericProjectConfig: FilePath) throws -> Self { + private let indexstorePaths: Set + private let plistPaths: Set + private let xibPaths: Set + private let xcDataModelsPaths: Set + private let xcMappingModelsPaths: Set + private let testTargets: Set + private let configuration: Configuration + + public convenience init(genericProjectConfig: FilePath, configuration: Configuration) throws { guard genericProjectConfig.exists else { throw PeripheryError.pathDoesNotExist(path: genericProjectConfig.string) } @@ -29,25 +37,17 @@ public final class GenericProjectDriver { let xcMappingModelPaths = config.xcmappingmodels.mapSet { FilePath.makeAbsolute($0) } let indexstorePaths = config.indexstores.mapSet { FilePath.makeAbsolute($0) } - return self.init( + self.init( indexstorePaths: indexstorePaths, plistPaths: plistPaths, xibPaths: xibPaths, xcDataModelsPaths: xcDataModelPaths, xcMappingModelsPaths: xcMappingModelPaths, testTargets: config.testTargets, - configuration: .shared + configuration: configuration ) } - private let indexstorePaths: Set - private let plistPaths: Set - private let xibPaths: Set - private let xcDataModelsPaths: Set - private let xcMappingModelsPaths: Set - private let testTargets: Set - private let configuration: Configuration - private init( indexstorePaths: Set, plistPaths: Set, @@ -73,7 +73,8 @@ extension GenericProjectDriver: ProjectDriver { let collector = SourceFileCollector( indexStorePaths: Set(configuration.indexStorePath).union(indexstorePaths), excludedTestTargets: excludedTestTargets, - logger: logger + logger: logger, + configuration: configuration ) let sourceFiles = try collector.collect() diff --git a/Sources/ProjectDrivers/SPMProjectDriver.swift b/Sources/ProjectDrivers/SPMProjectDriver.swift index a462fd0a0..520831699 100644 --- a/Sources/ProjectDrivers/SPMProjectDriver.swift +++ b/Sources/ProjectDrivers/SPMProjectDriver.swift @@ -5,22 +5,20 @@ import SwiftIndexStore import SystemPackage public final class SPMProjectDriver { - public static func build() throws -> Self { - let configuration = Configuration.shared + private let pkg: SPM.Package + private let configuration: Configuration + private let logger: Logger + public convenience init(configuration: Configuration, shell: Shell, logger: Logger) throws { if !configuration.schemes.isEmpty { throw PeripheryError.usageError("The --schemes option has no effect with Swift Package Manager projects.") } - let pkg = SPM.Package() - return self.init(pkg: pkg, configuration: configuration, logger: .init()) + let pkg = SPM.Package(configuration: configuration, shell: shell, logger: logger) + self.init(pkg: pkg, configuration: configuration, logger: logger) } - private let pkg: SPM.Package - private let configuration: Configuration - private let logger: Logger - - init(pkg: SPM.Package, configuration: Configuration, logger: Logger = .init()) { + init(pkg: SPM.Package, configuration: Configuration, logger: Logger) { self.pkg = pkg self.configuration = configuration self.logger = logger @@ -54,7 +52,8 @@ extension SPMProjectDriver: ProjectDriver { let collector = SourceFileCollector( indexStorePaths: indexStorePaths, excludedTestTargets: excludedTestTargets, - logger: logger + logger: logger, + configuration: configuration ) let sourceFiles = try collector.collect() diff --git a/Sources/ProjectDrivers/XcodeProjectDriver.swift b/Sources/ProjectDrivers/XcodeProjectDriver.swift index 1e52b1c59..77a6ca2d3 100644 --- a/Sources/ProjectDrivers/XcodeProjectDriver.swift +++ b/Sources/ProjectDrivers/XcodeProjectDriver.swift @@ -7,9 +7,19 @@ import XcodeSupport public final class XcodeProjectDriver { - public static func build(projectPath: FilePath) throws -> Self { - let configuration = Configuration.shared - let xcodebuild = Xcodebuild() + private let logger: Logger + private let configuration: Configuration + private let xcodebuild: Xcodebuild + private let project: XcodeProjectlike + private let schemes: Set + + public convenience init( + projectPath: FilePath, + configuration: Configuration, + shell: Shell, + logger: Logger + ) throws { + let xcodebuild = Xcodebuild(shell: shell, logger: logger) guard !configuration.schemes.isEmpty else { throw PeripheryError.usageError("The '--schemes' option is required.") @@ -18,9 +28,20 @@ try xcodebuild.ensureConfigured() let project: XcodeProjectlike = if projectPath.extension == "xcworkspace" { - try XcodeWorkspace(path: .makeAbsolute(projectPath)) + try XcodeWorkspace( + path: .makeAbsolute(projectPath), + xcodebuild: xcodebuild, + configuration: configuration, + logger: logger, + shell: shell + ) } else { - try XcodeProject(path: .makeAbsolute(projectPath)) + try XcodeProject( + path: .makeAbsolute(projectPath), + xcodebuild: xcodebuild, + shell: shell, + logger: logger + ) } let schemes: Set @@ -39,7 +60,8 @@ } } - return self.init( + self.init( + logger: logger, configuration: configuration, xcodebuild: xcodebuild, project: project, @@ -47,14 +69,8 @@ ) } - private let logger: Logger - private let configuration: Configuration - private let xcodebuild: Xcodebuild - private let project: XcodeProjectlike - private let schemes: Set - init( - logger: Logger = .init(), + logger: Logger, configuration: Configuration, xcodebuild: Xcodebuild, project: XcodeProjectlike, @@ -102,7 +118,8 @@ let collector = SourceFileCollector( indexStorePaths: indexStorePaths, excludedTestTargets: excludedTestTargets, - logger: logger + logger: logger, + configuration: configuration ) let sourceFiles = try collector.collect() let infoPlistPaths = targets.flatMapSet { $0.files(kind: .infoPlist) } diff --git a/Sources/Shared/Configuration.swift b/Sources/Shared/Configuration.swift index cf37c96dd..610e72da8 100644 --- a/Sources/Shared/Configuration.swift +++ b/Sources/Shared/Configuration.swift @@ -5,11 +5,8 @@ import Yams public final class Configuration { public static var defaultConfigurationFile = FilePath(".periphery.yml") - public static let shared = Configuration() - public init(logger: BaseLogger = .shared) { - self.logger = logger - } + public init() {} @Setting(key: "project", defaultValue: nil, setter: filePathSetter) public var project: FilePath? @@ -134,9 +131,6 @@ public final class Configuration { // Non user facing. public var guidedSetup: Bool = false - // Dependencies. - private var logger: BaseLogger // Must use BaseLogger as Logger depends upon Configuration. - public var hasNonDefaultValues: Bool { settings.contains(where: \.hasNonDefaultValue) } @@ -156,7 +150,7 @@ public final class Configuration { FileManager.default.createFile(atPath: path.string, contents: data) } - public func load(from path: FilePath?) throws { + public func load(from path: FilePath?, logger: Logger) throws { guard let path = try configurationPath(withUserProvided: path) else { return } let encodedYAML = try String(contentsOf: path.url) diff --git a/Sources/Shared/Extensions/FilePath+Glob.swift b/Sources/Shared/Extensions/FilePath+Glob.swift index 788f28276..2d32d8032 100644 --- a/Sources/Shared/Extensions/FilePath+Glob.swift +++ b/Sources/Shared/Extensions/FilePath+Glob.swift @@ -14,8 +14,7 @@ public extension FilePath { return Glob( pattern: absolutePattern, - excludedDirectories: [".build", "node_modules", ".gems", "gems", ".swiftpm"], - logger: Logger() + excludedDirectories: [".build", "node_modules", ".gems", "gems", ".swiftpm"] ).paths.mapSet { FilePath($0).lexicallyNormalized() } } } @@ -27,18 +26,15 @@ public extension FilePath { /// - When the pattern ends with a trailing slash, only directories are matched. private class Glob { private let excludedDirectories: [String] - private let logger: Logger private var isDirectoryCache: [String: Bool] = [:] fileprivate var paths: Set = [] init( pattern: String, - excludedDirectories: [String], - logger: Logger + excludedDirectories: [String] ) { self.excludedDirectories = excludedDirectories - self.logger = logger let hasTrailingGlobstarSlash = pattern.hasSuffix("**/") let includeFiles = !hasTrailingGlobstarSlash @@ -73,17 +69,10 @@ private class Glob { let firstPart = parts.removeFirst() var lastPart = parts.joined(separator: "**") - var directories: [URL] - - if FileManager.default.fileExists(atPath: firstPart) { - do { - directories = try exploreDirectories(url: URL(fileURLWithPath: firstPart)) - } catch { - directories = [] - logger.error("Error parsing file system item: \(error)") - } + var directories: [URL] = if FileManager.default.fileExists(atPath: firstPart) { + exploreDirectories(url: URL(fileURLWithPath: firstPart)) } else { - directories = [] + [] } // Include the globstar root directory ("dir/") in a pattern like "dir/**" or "dir/**/" @@ -104,8 +93,8 @@ private class Glob { return results } - private func exploreDirectories(url: URL) throws -> [URL] { - let subURLs = try FileManager.default.contentsOfDirectory(atPath: url.path).flatMap { subPath -> [URL] in + private func exploreDirectories(url: URL) -> [URL] { + let subURLs = try? FileManager.default.contentsOfDirectory(atPath: url.path).flatMap { subPath -> [URL] in if excludedDirectories.contains(subPath) { return [] } @@ -116,10 +105,10 @@ private class Glob { return [] } - return try exploreDirectories(url: subPathURL) + return exploreDirectories(url: subPathURL) } - return [url] + subURLs + return [url] + (subURLs ?? []) } private func isDirectory(path: String) -> Bool { diff --git a/Sources/Shared/Logger.swift b/Sources/Shared/Logger.swift index 2cff44074..f6fc4d510 100644 --- a/Sources/Shared/Logger.swift +++ b/Sources/Shared/Logger.swift @@ -38,48 +38,6 @@ public func colorize(_ text: String, _ color: ANSIColor) -> String { return "\(color.rawValue)\(text)\u{001B}[0;0m" } -public final class BaseLogger { - public static let shared = BaseLogger() - - @usableFromInline let outputQueue: DispatchQueue - - private init() { - outputQueue = DispatchQueue(label: "BaseLogger.outputQueue") - } - - @inlinable - func info(_ text: String) { - log(text, output: stdout) - } - - @inlinable - func debug(_ text: String) { - log(text, output: stdout) - } - - @inlinable - func warn(_ text: String, newlinePrefix: Bool = false) { - if newlinePrefix { - log("", output: stderr) - } - let text = colorize("warning: ", .boldYellow) + text - log(text, output: stderr) - } - - @inlinable - func error(_ text: String) { - let text = colorize("error: ", .boldRed) + text - log(text, output: stderr) - } - - // MARK: - Private - - @inlinable - func log(_ line: String, output: UnsafeMutablePointer) { - _ = outputQueue.sync { fputs(line + "\n", output) } - } -} - public final class Logger { public static func configureBuffering() { var info = stat() @@ -91,17 +49,17 @@ public final class Logger { } } - @usableFromInline let baseLogger: BaseLogger @usableFromInline let configuration: Configuration + @usableFromInline let outputQueue: DispatchQueue #if canImport(os) @usableFromInline let signposter = OSSignposter() #endif @inlinable - public required init(baseLogger: BaseLogger = .shared, configuration: Configuration = .shared) { - self.baseLogger = baseLogger + public required init(configuration: Configuration) { self.configuration = configuration + outputQueue = DispatchQueue(label: "Logger.outputQueue") } @inlinable @@ -112,24 +70,30 @@ public final class Logger { @inlinable public func info(_ text: String, canQuiet: Bool = true) { guard !(configuration.quiet && canQuiet) else { return } - baseLogger.info(text) + log(text, output: stdout) } @inlinable public func debug(_ text: String) { guard configuration.verbose else { return } - baseLogger.debug(text) + log(text, output: stdout) } @inlinable public func warn(_ text: String, newlinePrefix: Bool = false) { guard !configuration.quiet else { return } - baseLogger.warn(text, newlinePrefix: newlinePrefix) + if newlinePrefix { + log("", output: stderr) + } + let text = colorize("warning: ", .boldYellow) + text + log(text, output: stderr) } + // periphery:ignore @inlinable public func error(_ text: String) { - baseLogger.error(text) + let text = colorize("error: ", .boldRed) + text + log(text, output: stderr) } @inlinable @@ -149,6 +113,13 @@ public final class Logger { signposter.endInterval(interval.name, interval.state) #endif } + + // MARK: - Private + + @inlinable + func log(_ line: String, output: UnsafeMutablePointer) { + _ = outputQueue.sync { fputs(line + "\n", output) } + } } public struct ContextualLogger { diff --git a/Sources/Shared/SPM.swift b/Sources/Shared/SPM.swift index 307c880df..a15c0b172 100644 --- a/Sources/Shared/SPM.swift +++ b/Sources/Shared/SPM.swift @@ -2,28 +2,29 @@ import Foundation import SystemPackage public enum SPM { - static let packageFile = "Package.swift" - public static var isSupported: Bool { - Package().exists + FilePath.current.appending("Package.swift").exists } public struct Package { public let path: FilePath = .current - let configuration: Configuration = .shared - public init() {} + private let configuration: Configuration + private let shell: Shell + private let logger: Logger - var exists: Bool { - path.appending(packageFile).exists + public init(configuration: Configuration, shell: Shell, logger: Logger) { + self.configuration = configuration + self.shell = shell + self.logger = logger } public func clean() throws { - try Shell.shared.exec(["swift", "package", "clean"]) + try shell.exec(["swift", "package", "clean"]) } public func build(additionalArguments: [String]) throws { - try Shell.shared.exec(["swift", "build", "--build-tests"] + additionalArguments) + try shell.exec(["swift", "build", "--build-tests"] + additionalArguments) } public func testTargetNames() throws -> Set { @@ -34,14 +35,14 @@ public enum SPM { // MARK: - Private private func load() throws -> PackageDescription { - Logger().contextualized(with: "spm:package").debug("Loading \(FilePath.current)") + logger.contextualized(with: "spm:package").debug("Loading \(FilePath.current)") let jsonData: Data if let path = configuration.jsonPackageManifestPath { jsonData = try Data(contentsOf: path.url) } else { - let jsonString = try Shell.shared.exec(["swift", "package", "describe", "--type", "json"], stderr: false) + let jsonString = try shell.exec(["swift", "package", "describe", "--type", "json"], stderr: false) guard let data = jsonString.data(using: .utf8) else { throw PeripheryError.packageError(message: "Failed to read swift package description.") diff --git a/Sources/Shared/Shell.swift b/Sources/Shared/Shell.swift index 86c602c9b..d2c7339e4 100644 --- a/Sources/Shared/Shell.swift +++ b/Sources/Shared/Shell.swift @@ -1,14 +1,16 @@ import Foundation open class Shell { - public static let shared: Shell = .init(environment: ProcessInfo.processInfo.environment, logger: Logger()) - private var tasks: Set = [] private var tasksQueue = DispatchQueue(label: "Shell.tasksQueue") private let environment: [String: String] private let logger: ContextualLogger + public convenience init(logger: Logger) { + self.init(environment: ProcessInfo.processInfo.environment, logger: logger) + } + public required init(environment: [String: String], logger: Logger) { self.environment = environment self.logger = logger.contextualized(with: "shell") diff --git a/Sources/Shared/SwiftVersion.swift b/Sources/Shared/SwiftVersion.swift index dbc711867..110f95893 100644 --- a/Sources/Shared/SwiftVersion.swift +++ b/Sources/Shared/SwiftVersion.swift @@ -1,22 +1,20 @@ import Foundation public struct SwiftVersion { - public static let current = SwiftVersion() - static let minimumVersion = "5.9" public let version: VersionString public let fullVersion: String - init(shell: Shell = .shared) { + public init(shell: Shell) { fullVersion = try! shell.exec(["swift", "-version"]).trimmed // swiftlint:disable:this force_try version = try! SwiftVersionParser.parse(fullVersion) // swiftlint:disable:this force_try } public func validateVersion() throws { - if Self.current.version.isVersion(lessThan: Self.minimumVersion) { + if version.isVersion(lessThan: Self.minimumVersion) { throw PeripheryError.swiftVersionUnsupportedError( - version: SwiftVersion.current.fullVersion, + version: fullVersion, minimumVersion: Self.minimumVersion ) } diff --git a/Sources/SourceGraph/Mutators/AccessibilityCascader.swift b/Sources/SourceGraph/Mutators/AccessibilityCascader.swift index 8b12bb580..bb8feed12 100644 --- a/Sources/SourceGraph/Mutators/AccessibilityCascader.swift +++ b/Sources/SourceGraph/Mutators/AccessibilityCascader.swift @@ -4,7 +4,7 @@ import Shared final class AccessibilityCascader: SourceGraphMutator { private let graph: SourceGraph - required init(graph: SourceGraph, configuration _: Configuration) { + required init(graph: SourceGraph, configuration _: Configuration, swiftVersion _: SwiftVersion) { self.graph = graph } diff --git a/Sources/SourceGraph/Mutators/AncestralReferenceEliminator.swift b/Sources/SourceGraph/Mutators/AncestralReferenceEliminator.swift index dccc97cdf..a4ffd7eeb 100644 --- a/Sources/SourceGraph/Mutators/AncestralReferenceEliminator.swift +++ b/Sources/SourceGraph/Mutators/AncestralReferenceEliminator.swift @@ -4,7 +4,7 @@ import Shared final class AncestralReferenceEliminator: SourceGraphMutator { private let graph: SourceGraph - required init(graph: SourceGraph, configuration _: Configuration) { + required init(graph: SourceGraph, configuration _: Configuration, swiftVersion _: SwiftVersion) { self.graph = graph } diff --git a/Sources/SourceGraph/Mutators/AssetReferenceRetainer.swift b/Sources/SourceGraph/Mutators/AssetReferenceRetainer.swift index 3d96aef5a..08237e0e5 100644 --- a/Sources/SourceGraph/Mutators/AssetReferenceRetainer.swift +++ b/Sources/SourceGraph/Mutators/AssetReferenceRetainer.swift @@ -6,7 +6,7 @@ final class AssetReferenceRetainer: SourceGraphMutator { private let graph: SourceGraph private let interfaceBuilderPropertyRetainer: InterfaceBuilderPropertyRetainer - required init(graph: SourceGraph, configuration _: Configuration) { + required init(graph: SourceGraph, configuration _: Configuration, swiftVersion _: SwiftVersion) { self.graph = graph interfaceBuilderPropertyRetainer = .init(graph: graph) } diff --git a/Sources/SourceGraph/Mutators/AssignOnlyPropertyReferenceEliminator.swift b/Sources/SourceGraph/Mutators/AssignOnlyPropertyReferenceEliminator.swift index 64f1d58a7..e14fc2be3 100644 --- a/Sources/SourceGraph/Mutators/AssignOnlyPropertyReferenceEliminator.swift +++ b/Sources/SourceGraph/Mutators/AssignOnlyPropertyReferenceEliminator.swift @@ -8,7 +8,7 @@ final class AssignOnlyPropertyReferenceEliminator: SourceGraphMutator { private let defaultRetainedTypes = ["AnyCancellable", "Set", "[AnyCancellable]", "NSKeyValueObservation"] private let retainedAttributes = ["State", "Binding"] - required init(graph: SourceGraph, configuration: Configuration) { + required init(graph: SourceGraph, configuration: Configuration, swiftVersion _: SwiftVersion) { self.graph = graph self.configuration = configuration retainAssignOnlyPropertyTypes = defaultRetainedTypes + configuration.retainAssignOnlyPropertyTypes diff --git a/Sources/SourceGraph/Mutators/CapitalSelfFunctionCallRetainer.swift b/Sources/SourceGraph/Mutators/CapitalSelfFunctionCallRetainer.swift index 8c3093ae9..24c62f482 100644 --- a/Sources/SourceGraph/Mutators/CapitalSelfFunctionCallRetainer.swift +++ b/Sources/SourceGraph/Mutators/CapitalSelfFunctionCallRetainer.swift @@ -6,13 +6,15 @@ import Shared /// https://github.com/peripheryapp/periphery/issues/264 final class CapitalSelfFunctionCallRetainer: SourceGraphMutator { private let graph: SourceGraph + private let swiftVersion: SwiftVersion - required init(graph: SourceGraph, configuration _: Configuration) { + required init(graph: SourceGraph, configuration _: Configuration, swiftVersion: SwiftVersion) { self.graph = graph + self.swiftVersion = swiftVersion } func mutate() { - guard SwiftVersion.current.version.isVersion(lessThan: "5.9") else { return } + guard swiftVersion.version.isVersion(lessThan: "5.9") else { return } for decl in graph.declarations(ofKinds: [.struct, .class]) { guard decl.hasCapitalSelfFunctionCall else { continue } diff --git a/Sources/SourceGraph/Mutators/CodablePropertyRetainer.swift b/Sources/SourceGraph/Mutators/CodablePropertyRetainer.swift index fa67174ac..485cf7ae5 100644 --- a/Sources/SourceGraph/Mutators/CodablePropertyRetainer.swift +++ b/Sources/SourceGraph/Mutators/CodablePropertyRetainer.swift @@ -5,7 +5,7 @@ final class CodablePropertyRetainer: SourceGraphMutator { private let graph: SourceGraph private let configuration: Configuration - required init(graph: SourceGraph, configuration: Configuration) { + required init(graph: SourceGraph, configuration: Configuration, swiftVersion _: SwiftVersion) { self.graph = graph self.configuration = configuration } diff --git a/Sources/SourceGraph/Mutators/CodingKeyEnumReferenceBuilder.swift b/Sources/SourceGraph/Mutators/CodingKeyEnumReferenceBuilder.swift index 163d7864a..4b57d97ec 100644 --- a/Sources/SourceGraph/Mutators/CodingKeyEnumReferenceBuilder.swift +++ b/Sources/SourceGraph/Mutators/CodingKeyEnumReferenceBuilder.swift @@ -5,7 +5,7 @@ import Shared final class CodingKeyEnumReferenceBuilder: SourceGraphMutator { private let graph: SourceGraph - required init(graph: SourceGraph, configuration _: Configuration) { + required init(graph: SourceGraph, configuration _: Configuration, swiftVersion _: SwiftVersion) { self.graph = graph } diff --git a/Sources/SourceGraph/Mutators/ComplexPropertyAccessorReferenceBuilder.swift b/Sources/SourceGraph/Mutators/ComplexPropertyAccessorReferenceBuilder.swift index ed7c65917..c616c4d4f 100644 --- a/Sources/SourceGraph/Mutators/ComplexPropertyAccessorReferenceBuilder.swift +++ b/Sources/SourceGraph/Mutators/ComplexPropertyAccessorReferenceBuilder.swift @@ -8,7 +8,7 @@ import Shared final class ComplexPropertyAccessorReferenceBuilder: SourceGraphMutator { private let graph: SourceGraph - required init(graph: SourceGraph, configuration _: Configuration) { + required init(graph: SourceGraph, configuration _: Configuration, swiftVersion _: SwiftVersion) { self.graph = graph } diff --git a/Sources/SourceGraph/Mutators/DefaultConstructorReferenceBuilder.swift b/Sources/SourceGraph/Mutators/DefaultConstructorReferenceBuilder.swift index e17a7f7ee..49f69ab4a 100644 --- a/Sources/SourceGraph/Mutators/DefaultConstructorReferenceBuilder.swift +++ b/Sources/SourceGraph/Mutators/DefaultConstructorReferenceBuilder.swift @@ -4,7 +4,7 @@ import Shared final class DefaultConstructorReferenceBuilder: SourceGraphMutator { private let graph: SourceGraph - required init(graph: SourceGraph, configuration _: Configuration) { + required init(graph: SourceGraph, configuration _: Configuration, swiftVersion _: SwiftVersion) { self.graph = graph } diff --git a/Sources/SourceGraph/Mutators/DynamicMemberLookupReferenceBuilder.swift b/Sources/SourceGraph/Mutators/DynamicMemberLookupReferenceBuilder.swift index 202201fa4..a812ef4fc 100644 --- a/Sources/SourceGraph/Mutators/DynamicMemberLookupReferenceBuilder.swift +++ b/Sources/SourceGraph/Mutators/DynamicMemberLookupReferenceBuilder.swift @@ -4,7 +4,7 @@ import Shared final class DynamicMemberLookupReferenceBuilder: SourceGraphMutator { private let graph: SourceGraph - required init(graph: SourceGraph, configuration _: Configuration) { + required init(graph: SourceGraph, configuration _: Configuration, swiftVersion _: SwiftVersion) { self.graph = graph } diff --git a/Sources/SourceGraph/Mutators/EntryPointAttributeRetainer.swift b/Sources/SourceGraph/Mutators/EntryPointAttributeRetainer.swift index 9e4ced96e..47ea5f173 100644 --- a/Sources/SourceGraph/Mutators/EntryPointAttributeRetainer.swift +++ b/Sources/SourceGraph/Mutators/EntryPointAttributeRetainer.swift @@ -4,7 +4,7 @@ import Shared final class EntryPointAttributeRetainer: SourceGraphMutator { private let graph: SourceGraph - required init(graph: SourceGraph, configuration _: Configuration) { + required init(graph: SourceGraph, configuration _: Configuration, swiftVersion _: SwiftVersion) { self.graph = graph } diff --git a/Sources/SourceGraph/Mutators/EnumCaseReferenceBuilder.swift b/Sources/SourceGraph/Mutators/EnumCaseReferenceBuilder.swift index 468e9535e..9bfff0e71 100644 --- a/Sources/SourceGraph/Mutators/EnumCaseReferenceBuilder.swift +++ b/Sources/SourceGraph/Mutators/EnumCaseReferenceBuilder.swift @@ -5,7 +5,7 @@ import Shared final class EnumCaseReferenceBuilder: SourceGraphMutator { private let graph: SourceGraph - required init(graph: SourceGraph, configuration _: Configuration) { + required init(graph: SourceGraph, configuration _: Configuration, swiftVersion _: SwiftVersion) { self.graph = graph } diff --git a/Sources/SourceGraph/Mutators/ExtensionReferenceBuilder.swift b/Sources/SourceGraph/Mutators/ExtensionReferenceBuilder.swift index 1402763d4..ebd9a61bc 100644 --- a/Sources/SourceGraph/Mutators/ExtensionReferenceBuilder.swift +++ b/Sources/SourceGraph/Mutators/ExtensionReferenceBuilder.swift @@ -6,7 +6,7 @@ import Shared final class ExtensionReferenceBuilder: SourceGraphMutator { private let graph: SourceGraph - required init(graph: SourceGraph, configuration _: Configuration) { + required init(graph: SourceGraph, configuration _: Configuration, swiftVersion _: SwiftVersion) { self.graph = graph } diff --git a/Sources/SourceGraph/Mutators/ExternalTypeProtocolConformanceReferenceRemover.swift b/Sources/SourceGraph/Mutators/ExternalTypeProtocolConformanceReferenceRemover.swift index b3db554bb..adb61b7d5 100644 --- a/Sources/SourceGraph/Mutators/ExternalTypeProtocolConformanceReferenceRemover.swift +++ b/Sources/SourceGraph/Mutators/ExternalTypeProtocolConformanceReferenceRemover.swift @@ -4,7 +4,7 @@ import Shared final class ExternalTypeProtocolConformanceReferenceRemover: SourceGraphMutator { private let graph: SourceGraph - required init(graph: SourceGraph, configuration _: Configuration) { + required init(graph: SourceGraph, configuration _: Configuration, swiftVersion _: SwiftVersion) { self.graph = graph } diff --git a/Sources/SourceGraph/Mutators/GenericClassAndStructConstructorReferenceBuilder.swift b/Sources/SourceGraph/Mutators/GenericClassAndStructConstructorReferenceBuilder.swift index f59a0fb99..4bac17c0e 100644 --- a/Sources/SourceGraph/Mutators/GenericClassAndStructConstructorReferenceBuilder.swift +++ b/Sources/SourceGraph/Mutators/GenericClassAndStructConstructorReferenceBuilder.swift @@ -7,7 +7,7 @@ import Shared final class GenericClassAndStructConstructorReferenceBuilder: SourceGraphMutator { private let graph: SourceGraph - required init(graph: SourceGraph, configuration _: Configuration) { + required init(graph: SourceGraph, configuration _: Configuration, swiftVersion _: SwiftVersion) { self.graph = graph } diff --git a/Sources/SourceGraph/Mutators/ObjCAccessibleRetainer.swift b/Sources/SourceGraph/Mutators/ObjCAccessibleRetainer.swift index cc013a5d5..6ee74e5fd 100644 --- a/Sources/SourceGraph/Mutators/ObjCAccessibleRetainer.swift +++ b/Sources/SourceGraph/Mutators/ObjCAccessibleRetainer.swift @@ -5,7 +5,7 @@ final class ObjCAccessibleRetainer: SourceGraphMutator { private let graph: SourceGraph private let configuration: Configuration - required init(graph: SourceGraph, configuration: Configuration) { + required init(graph: SourceGraph, configuration: Configuration, swiftVersion _: SwiftVersion) { self.graph = graph self.configuration = configuration } diff --git a/Sources/SourceGraph/Mutators/PropertyWrapperRetainer.swift b/Sources/SourceGraph/Mutators/PropertyWrapperRetainer.swift index a3a313d27..69e07e153 100644 --- a/Sources/SourceGraph/Mutators/PropertyWrapperRetainer.swift +++ b/Sources/SourceGraph/Mutators/PropertyWrapperRetainer.swift @@ -5,7 +5,7 @@ final class PropertyWrapperRetainer: SourceGraphMutator { private let graph: SourceGraph private let specialProperties = ["wrappedValue", "projectedValue"] - required init(graph: SourceGraph, configuration _: Configuration) { + required init(graph: SourceGraph, configuration _: Configuration, swiftVersion _: SwiftVersion) { self.graph = graph } diff --git a/Sources/SourceGraph/Mutators/ProtocolConformanceReferenceBuilder.swift b/Sources/SourceGraph/Mutators/ProtocolConformanceReferenceBuilder.swift index 36d4a505e..dd0623335 100644 --- a/Sources/SourceGraph/Mutators/ProtocolConformanceReferenceBuilder.swift +++ b/Sources/SourceGraph/Mutators/ProtocolConformanceReferenceBuilder.swift @@ -4,7 +4,7 @@ import Shared final class ProtocolConformanceReferenceBuilder: SourceGraphMutator { private let graph: SourceGraph - required init(graph: SourceGraph, configuration _: Configuration) { + required init(graph: SourceGraph, configuration _: Configuration, swiftVersion _: SwiftVersion) { self.graph = graph } diff --git a/Sources/SourceGraph/Mutators/ProtocolExtensionReferenceBuilder.swift b/Sources/SourceGraph/Mutators/ProtocolExtensionReferenceBuilder.swift index 46681dead..f93da5d76 100644 --- a/Sources/SourceGraph/Mutators/ProtocolExtensionReferenceBuilder.swift +++ b/Sources/SourceGraph/Mutators/ProtocolExtensionReferenceBuilder.swift @@ -4,7 +4,7 @@ import Shared final class ProtocolExtensionReferenceBuilder: SourceGraphMutator { private let graph: SourceGraph - required init(graph: SourceGraph, configuration _: Configuration) { + required init(graph: SourceGraph, configuration _: Configuration, swiftVersion _: SwiftVersion) { self.graph = graph } diff --git a/Sources/SourceGraph/Mutators/PubliclyAccessibleRetainer.swift b/Sources/SourceGraph/Mutators/PubliclyAccessibleRetainer.swift index 80e8ca712..dfb97d2f2 100644 --- a/Sources/SourceGraph/Mutators/PubliclyAccessibleRetainer.swift +++ b/Sources/SourceGraph/Mutators/PubliclyAccessibleRetainer.swift @@ -5,7 +5,7 @@ final class PubliclyAccessibleRetainer: SourceGraphMutator { private let graph: SourceGraph private let configuration: Configuration - required init(graph: SourceGraph, configuration: Configuration) { + required init(graph: SourceGraph, configuration: Configuration, swiftVersion _: SwiftVersion) { self.graph = graph self.configuration = configuration } diff --git a/Sources/SourceGraph/Mutators/RedundantExplicitPublicAccessibilityMarker.swift b/Sources/SourceGraph/Mutators/RedundantExplicitPublicAccessibilityMarker.swift index 7bc0becaf..9e7ef3914 100644 --- a/Sources/SourceGraph/Mutators/RedundantExplicitPublicAccessibilityMarker.swift +++ b/Sources/SourceGraph/Mutators/RedundantExplicitPublicAccessibilityMarker.swift @@ -4,7 +4,7 @@ final class RedundantExplicitPublicAccessibilityMarker: SourceGraphMutator { private let graph: SourceGraph private let configuration: Configuration - required init(graph: SourceGraph, configuration: Configuration) { + required init(graph: SourceGraph, configuration: Configuration, swiftVersion _: SwiftVersion) { self.graph = graph self.configuration = configuration } diff --git a/Sources/SourceGraph/Mutators/RedundantProtocolMarker.swift b/Sources/SourceGraph/Mutators/RedundantProtocolMarker.swift index 4d3caf318..49ad3f128 100644 --- a/Sources/SourceGraph/Mutators/RedundantProtocolMarker.swift +++ b/Sources/SourceGraph/Mutators/RedundantProtocolMarker.swift @@ -5,7 +5,7 @@ import Shared final class RedundantProtocolMarker: SourceGraphMutator { private let graph: SourceGraph - required init(graph: SourceGraph, configuration _: Configuration) { + required init(graph: SourceGraph, configuration _: Configuration, swiftVersion _: SwiftVersion) { self.graph = graph } diff --git a/Sources/SourceGraph/Mutators/ResultBuilderRetainer.swift b/Sources/SourceGraph/Mutators/ResultBuilderRetainer.swift index 055c4bf21..04915b994 100644 --- a/Sources/SourceGraph/Mutators/ResultBuilderRetainer.swift +++ b/Sources/SourceGraph/Mutators/ResultBuilderRetainer.swift @@ -15,7 +15,7 @@ final class ResultBuilderRetainer: SourceGraphMutator { "buildLimitedAvailability(_:)", ]) - required init(graph: SourceGraph, configuration _: Configuration) { + required init(graph: SourceGraph, configuration _: Configuration, swiftVersion _: SwiftVersion) { self.graph = graph } diff --git a/Sources/SourceGraph/Mutators/StringInterpolationAppendInterpolationRetainer.swift b/Sources/SourceGraph/Mutators/StringInterpolationAppendInterpolationRetainer.swift index 46c42ae26..96e64714c 100644 --- a/Sources/SourceGraph/Mutators/StringInterpolationAppendInterpolationRetainer.swift +++ b/Sources/SourceGraph/Mutators/StringInterpolationAppendInterpolationRetainer.swift @@ -6,7 +6,7 @@ import Shared final class StringInterpolationAppendInterpolationRetainer: SourceGraphMutator { private let graph: SourceGraph - required init(graph: SourceGraph, configuration _: Configuration) { + required init(graph: SourceGraph, configuration _: Configuration, swiftVersion _: SwiftVersion) { self.graph = graph } diff --git a/Sources/SourceGraph/Mutators/StructImplicitInitializerReferenceBuilder.swift b/Sources/SourceGraph/Mutators/StructImplicitInitializerReferenceBuilder.swift index ed6852423..fb793d19c 100644 --- a/Sources/SourceGraph/Mutators/StructImplicitInitializerReferenceBuilder.swift +++ b/Sources/SourceGraph/Mutators/StructImplicitInitializerReferenceBuilder.swift @@ -5,7 +5,7 @@ import Shared final class StructImplicitInitializerReferenceBuilder: SourceGraphMutator { private let graph: SourceGraph - init(graph: SourceGraph, configuration _: Configuration) { + init(graph: SourceGraph, configuration _: Configuration, swiftVersion _: SwiftVersion) { self.graph = graph } diff --git a/Sources/SourceGraph/Mutators/SwiftUIRetainer.swift b/Sources/SourceGraph/Mutators/SwiftUIRetainer.swift index 646f8d7e8..a76fec8c7 100644 --- a/Sources/SourceGraph/Mutators/SwiftUIRetainer.swift +++ b/Sources/SourceGraph/Mutators/SwiftUIRetainer.swift @@ -7,7 +7,7 @@ final class SwiftUIRetainer: SourceGraphMutator { private static let specialProtocolNames = ["LibraryContentProvider"] private static let applicationDelegateAdaptorStructNames = ["UIApplicationDelegateAdaptor", "NSApplicationDelegateAdaptor"] - required init(graph: SourceGraph, configuration: Configuration) { + required init(graph: SourceGraph, configuration: Configuration, swiftVersion _: SwiftVersion) { self.graph = graph self.configuration = configuration } diff --git a/Sources/SourceGraph/Mutators/UnusedImportMarker.swift b/Sources/SourceGraph/Mutators/UnusedImportMarker.swift index 73d469702..4359800f4 100644 --- a/Sources/SourceGraph/Mutators/UnusedImportMarker.swift +++ b/Sources/SourceGraph/Mutators/UnusedImportMarker.swift @@ -13,7 +13,7 @@ final class UnusedImportMarker: SourceGraphMutator { private let graph: SourceGraph private let configuration: Configuration - required init(graph: SourceGraph, configuration: Configuration) { + required init(graph: SourceGraph, configuration: Configuration, swiftVersion _: SwiftVersion) { self.graph = graph self.configuration = configuration } diff --git a/Sources/SourceGraph/Mutators/UnusedParameterRetainer.swift b/Sources/SourceGraph/Mutators/UnusedParameterRetainer.swift index 1d84f2afe..68d22c559 100644 --- a/Sources/SourceGraph/Mutators/UnusedParameterRetainer.swift +++ b/Sources/SourceGraph/Mutators/UnusedParameterRetainer.swift @@ -5,7 +5,7 @@ final class UnusedParameterRetainer: SourceGraphMutator { private let graph: SourceGraph private let configuration: Configuration - required init(graph: SourceGraph, configuration: Configuration) { + required init(graph: SourceGraph, configuration: Configuration, swiftVersion _: SwiftVersion) { self.graph = graph self.configuration = configuration } diff --git a/Sources/SourceGraph/Mutators/UsedDeclarationMarker.swift b/Sources/SourceGraph/Mutators/UsedDeclarationMarker.swift index f7bc024d7..852ef82c0 100644 --- a/Sources/SourceGraph/Mutators/UsedDeclarationMarker.swift +++ b/Sources/SourceGraph/Mutators/UsedDeclarationMarker.swift @@ -4,7 +4,7 @@ import Shared final class UsedDeclarationMarker: SourceGraphMutator { private let graph: SourceGraph - required init(graph: SourceGraph, configuration _: Configuration) { + required init(graph: SourceGraph, configuration _: Configuration, swiftVersion _: SwiftVersion) { self.graph = graph } diff --git a/Sources/SourceGraph/Mutators/XCTestRetainer.swift b/Sources/SourceGraph/Mutators/XCTestRetainer.swift index 5a0242766..057bee7cf 100644 --- a/Sources/SourceGraph/Mutators/XCTestRetainer.swift +++ b/Sources/SourceGraph/Mutators/XCTestRetainer.swift @@ -5,7 +5,7 @@ final class XCTestRetainer: SourceGraphMutator { private let graph: SourceGraph private let testCaseClassNames: Set - required init(graph: SourceGraph, configuration: Configuration) { + required init(graph: SourceGraph, configuration: Configuration, swiftVersion _: SwiftVersion) { self.graph = graph testCaseClassNames = Set(configuration.externalTestCaseClasses + ["XCTestCase"]) } diff --git a/Sources/SourceGraph/SourceGraph.swift b/Sources/SourceGraph/SourceGraph.swift index 668bf8a10..010b5f805 100644 --- a/Sources/SourceGraph/SourceGraph.swift +++ b/Sources/SourceGraph/SourceGraph.swift @@ -2,9 +2,6 @@ import Foundation import Shared public final class SourceGraph { - // Global shared instance to prevent costly deinitialization. - public static var shared = SourceGraph() - public private(set) var allDeclarations: Set = [] public private(set) var usedDeclarations: Set = [] public private(set) var redundantProtocols: [Declaration: (references: Set, inherited: Set)] = [:] @@ -29,7 +26,7 @@ public final class SourceGraph { private let configuration: Configuration - init(configuration: Configuration = .shared) { + public init(configuration: Configuration) { self.configuration = configuration } diff --git a/Sources/SourceGraph/SourceGraphMutator.swift b/Sources/SourceGraph/SourceGraphMutator.swift index 4d7080531..8cde225a3 100644 --- a/Sources/SourceGraph/SourceGraphMutator.swift +++ b/Sources/SourceGraph/SourceGraphMutator.swift @@ -2,6 +2,6 @@ import Foundation import Shared protocol SourceGraphMutator: AnyObject { - init(graph: SourceGraph, configuration: Configuration) + init(graph: SourceGraph, configuration: Configuration, swiftVersion: SwiftVersion) func mutate() throws } diff --git a/Sources/SourceGraph/SourceGraphMutatorRunner.swift b/Sources/SourceGraph/SourceGraphMutatorRunner.swift index 311fd9bf0..7b4508721 100644 --- a/Sources/SourceGraph/SourceGraphMutatorRunner.swift +++ b/Sources/SourceGraph/SourceGraphMutatorRunner.swift @@ -2,10 +2,6 @@ import Foundation import Shared public final class SourceGraphMutatorRunner { - public static func perform(graph: SourceGraph) throws { - try self.init(graph: graph).perform() - } - private let mutators: [SourceGraphMutator.Type] = [ // Must come before all others as we need to observe all references prior to any mutations. UnusedImportMarker.self, @@ -55,18 +51,20 @@ public final class SourceGraphMutatorRunner { private let graph: SourceGraph private let logger: ContextualLogger private let configuration: Configuration + private let swiftVersion: SwiftVersion - required init(graph: SourceGraph, logger: Logger = .init(), configuration: Configuration = .shared) { + public required init(graph: SourceGraph, logger: Logger, configuration: Configuration, swiftVersion: SwiftVersion) { self.graph = graph self.logger = logger.contextualized(with: "mutator:run") self.configuration = configuration + self.swiftVersion = swiftVersion } - func perform() throws { + public func perform() throws { for mutator in mutators { let elapsed = try Benchmark.measure { let interval = logger.beginInterval("mutator:run") - try mutator.init(graph: graph, configuration: configuration).mutate() + try mutator.init(graph: graph, configuration: configuration, swiftVersion: swiftVersion).mutate() logger.endInterval(interval) } logger.debug("\(mutator) (\(elapsed)s)") diff --git a/Sources/XcodeSupport/XcodeProject.swift b/Sources/XcodeSupport/XcodeProject.swift index de3b8f3a6..80d593f0c 100644 --- a/Sources/XcodeSupport/XcodeProject.swift +++ b/Sources/XcodeSupport/XcodeProject.swift @@ -6,24 +6,6 @@ import XcodeProj public final class XcodeProject: XcodeProjectlike { private static var cache: [FilePath: XcodeProject] = [:] - static func build(path: FilePath, referencedBy refPath: FilePath) throws -> XcodeProject? { - if !path.exists { - let logger = Logger() - logger.warn("No such project exists at '\(path.lexicallyNormalized())', referenced by '\(refPath)'.") - return nil - } - - return try build(path: path) - } - - static func build(path: FilePath) throws -> XcodeProject { - if let cached = cache[path] { - return cached - } - - return try self.init(path: path) - } - public let type: String = "project" public let path: FilePath public let sourceRoot: FilePath @@ -34,7 +16,17 @@ public final class XcodeProject: XcodeProjectlike { private let xcodebuild: Xcodebuild - public required init(path: FilePath, xcodebuild: Xcodebuild = .init(), logger: Logger = .init()) throws { + convenience init?(path: FilePath, referencedBy refPath: FilePath, shell: Shell, logger: Logger) throws { + if !path.exists { + logger.warn("No such project exists at '\(path.lexicallyNormalized())', referenced by '\(refPath)'.") + return nil + } + + let xcodebuild = Xcodebuild(shell: shell, logger: logger) + try self.init(path: path, xcodebuild: xcodebuild, shell: shell, logger: logger) + } + + public required init(path: FilePath, xcodebuild: Xcodebuild, shell: Shell, logger: Logger) throws { logger.contextualized(with: "xcode:project").debug("Loading \(path)") self.path = path @@ -58,7 +50,7 @@ public final class XcodeProject: XcodeProjectlike { subProjects = try xcodeProject.pbxproj.fileReferences .filter { $0.path?.hasSuffix(".xcodeproj") ?? false } .compactMap { try $0.fullPath(sourceRoot: sourceRoot.string) } - .compactMap { try XcodeProject.build(path: FilePath($0), referencedBy: path) } + .compactMap { try XcodeProject(path: FilePath($0), referencedBy: path, shell: shell, logger: logger) } } targets = xcodeProject.pbxproj.nativeTargets diff --git a/Sources/XcodeSupport/XcodeProjectSetupGuide.swift b/Sources/XcodeSupport/XcodeProjectSetupGuide.swift index 88bf394ad..53abef853 100644 --- a/Sources/XcodeSupport/XcodeProjectSetupGuide.swift +++ b/Sources/XcodeSupport/XcodeProjectSetupGuide.swift @@ -3,7 +3,14 @@ import Shared import SystemPackage public final class XcodeProjectSetupGuide: SetupGuideHelpers, SetupGuide { - public static func detect() -> Self? { + private let workspacePaths: Set + private let projectPaths: Set + private let configuration: Configuration + private let logger: Logger + private let shell: Shell + private let xcodebuild: Xcodebuild + + public convenience init?(configuration: Configuration, shell: Shell, logger: Logger) { let workspacePaths = FilePath .glob("**/*.xcworkspace") .filter { @@ -16,27 +23,28 @@ public final class XcodeProjectSetupGuide: SetupGuideHelpers, SetupGuide { return nil } - return Self( + self.init( workspacePaths: workspacePaths, - projectPaths: projectPaths + projectPaths: projectPaths, + configuration: configuration, + shell: shell, + logger: logger ) } - private let workspacePaths: Set - private let projectPaths: Set - private let configuration: Configuration - private let xcodebuild: Xcodebuild - public required init( workspacePaths: Set, projectPaths: Set, - configuration: Configuration = .shared, - xcodebuild: Xcodebuild = .init() + configuration: Configuration, + shell: Shell, + logger: Logger ) { self.workspacePaths = workspacePaths self.projectPaths = projectPaths self.configuration = configuration - self.xcodebuild = xcodebuild + self.logger = logger + self.shell = shell + xcodebuild = Xcodebuild(shell: shell, logger: logger) super.init() } @@ -50,9 +58,20 @@ public final class XcodeProjectSetupGuide: SetupGuideHelpers, SetupGuide { var project: XcodeProjectlike? if let workspacePath = identifyWorkspace() { - project = try XcodeWorkspace(path: workspacePath) + project = try XcodeWorkspace( + path: workspacePath, + xcodebuild: xcodebuild, + configuration: configuration, + logger: logger, + shell: shell + ) } else if let projectPath = identifyProject() { - project = try XcodeProject(path: projectPath) + project = try XcodeProject( + path: projectPath, + xcodebuild: xcodebuild, + shell: shell, + logger: logger + ) } guard let project else { diff --git a/Sources/XcodeSupport/XcodeWorkspace.swift b/Sources/XcodeSupport/XcodeWorkspace.swift index f81be4a31..65a49d944 100644 --- a/Sources/XcodeSupport/XcodeWorkspace.swift +++ b/Sources/XcodeSupport/XcodeWorkspace.swift @@ -14,7 +14,7 @@ public final class XcodeWorkspace: XcodeProjectlike { public private(set) var targets: Set = [] - public required init(path: FilePath, xcodebuild: Xcodebuild = .init(), configuration: Configuration = .shared, logger: Logger = .init()) throws { + public required init(path: FilePath, xcodebuild: Xcodebuild, configuration: Configuration, logger: Logger, shell: Shell) throws { logger.contextualized(with: "xcode:workspace").debug("Loading \(path)") self.path = path @@ -29,7 +29,7 @@ public final class XcodeWorkspace: XcodeProjectlike { } let projectPaths = collectProjectPaths(in: xcworkspace.data.children) - let projects = try projectPaths.compactMapSet { try XcodeProject.build(path: sourceRoot.pushing($0), referencedBy: self.path) } + let projects = try projectPaths.compactMapSet { try XcodeProject(path: sourceRoot.pushing($0), referencedBy: self.path, shell: shell, logger: logger) } targets = projects.reduce(into: .init()) { result, project in result.formUnion(project.targets) diff --git a/Sources/XcodeSupport/Xcodebuild.swift b/Sources/XcodeSupport/Xcodebuild.swift index 5a2117dac..e15969b31 100644 --- a/Sources/XcodeSupport/Xcodebuild.swift +++ b/Sources/XcodeSupport/Xcodebuild.swift @@ -6,7 +6,7 @@ public final class Xcodebuild { private let shell: Shell private let logger: Logger - public required init(shell: Shell = .shared, logger: Logger = .init()) { + public required init(shell: Shell, logger: Logger) { self.shell = shell self.logger = logger } diff --git a/Tests/PeripheryTests/RetentionTest.swift b/Tests/PeripheryTests/RetentionTest.swift index b42c25dde..4c326372d 100644 --- a/Tests/PeripheryTests/RetentionTest.swift +++ b/Tests/PeripheryTests/RetentionTest.swift @@ -550,7 +550,6 @@ final class RetentionTest: FixtureSourceGraphTestCase { } func testCodingKeyEnum() { - let configuration = Configuration.shared // CustomStringConvertible doesn't actually inherit Codable, we're just using it because we don't have an // external module in which to declare our own type. configuration.externalCodableProtocols = ["CustomStringConvertible"] diff --git a/Tests/PeripheryTests/ShellTest.swift b/Tests/PeripheryTests/ShellTest.swift index 88b091bc5..2adb009ac 100644 --- a/Tests/PeripheryTests/ShellTest.swift +++ b/Tests/PeripheryTests/ShellTest.swift @@ -9,7 +9,9 @@ final class ShellTest: XCTestCase { "PATH": path, "DEVELOPER_DIR": developerDir, ] - let shell = Shell(environment: environment, logger: .init()) + let configuration = Configuration() + let logger = Logger(configuration: configuration) + let shell = Shell(environment: environment, logger: logger) XCTAssertEqual(shell.pristineEnvironment["PATH"], path) XCTAssertEqual(shell.pristineEnvironment["DEVELOPER_DIR"], developerDir) } diff --git a/Tests/Shared/SPMSourceGraphTestCase.swift b/Tests/Shared/SPMSourceGraphTestCase.swift index 8275abd44..4eec86920 100644 --- a/Tests/Shared/SPMSourceGraphTestCase.swift +++ b/Tests/Shared/SPMSourceGraphTestCase.swift @@ -6,9 +6,9 @@ import SystemPackage class SPMSourceGraphTestCase: SourceGraphTestCase { static func build(projectPath: FilePath = ProjectRootPath) { projectPath.chdir { - let driver = try! SPMProjectDriver.build() + let driver = try! SPMProjectDriver(configuration: configuration, shell: shell, logger: logger) try! driver.build() - plan = try! driver.plan(logger: Logger().contextualized(with: "index")) + plan = try! driver.plan(logger: logger.contextualized(with: "index")) } } } diff --git a/Tests/Shared/SourceGraphTestCase.swift b/Tests/Shared/SourceGraphTestCase.swift index 89376d669..899ddebf2 100644 --- a/Tests/Shared/SourceGraphTestCase.swift +++ b/Tests/Shared/SourceGraphTestCase.swift @@ -8,9 +8,11 @@ import XCTest open class SourceGraphTestCase: XCTestCase { static var plan: IndexPlan! static var configuration: Configuration! + static var shell: Shell! + static var logger: Logger! static var results: [ScanResult] = [] - private static var graph = SourceGraph() + private static var graph: SourceGraph! private static var allIndexedDeclarations: Set = [] var configuration: Configuration { Self.configuration } @@ -19,8 +21,11 @@ open class SourceGraphTestCase: XCTestCase { override open class func setUp() { super.setUp() - configuration = Configuration.shared + configuration = Configuration() configuration.quiet = true + logger = Logger(configuration: configuration) + shell = Shell(logger: logger) + graph = SourceGraph(configuration: configuration) } override open func setUp() { @@ -51,16 +56,22 @@ open class SourceGraphTestCase: XCTestCase { ) } - graph = SourceGraph() + graph = SourceGraph(configuration: configuration) let pipeline = IndexPipeline( plan: newPlan, graph: SynchronizedSourceGraph(graph: graph), - logger: Logger().contextualized(with: "index") + logger: logger.contextualized(with: "index"), + configuration: configuration ) try! pipeline.perform() allIndexedDeclarations = graph.allDeclarations - try! SourceGraphMutatorRunner.perform(graph: graph) + try! SourceGraphMutatorRunner( + graph: graph, + logger: logger, + configuration: configuration, + swiftVersion: SwiftVersion(shell: shell) + ).perform() results = ScanResultBuilder.build(for: graph) } diff --git a/Tests/XcodeTests/ShellMock.swift b/Tests/XcodeTests/ShellMock.swift index 6a7e23acc..045825df2 100644 --- a/Tests/XcodeTests/ShellMock.swift +++ b/Tests/XcodeTests/ShellMock.swift @@ -5,7 +5,9 @@ class ShellMock: Shell { var output: String = "" convenience init() { - self.init(environment: ProcessInfo.processInfo.environment, logger: Logger()) + let configuration = Configuration() + let logger = Logger(configuration: configuration) + self.init(environment: ProcessInfo.processInfo.environment, logger: logger) } override func exec(_: [String], stderr _: Bool = true) throws -> String { diff --git a/Tests/XcodeTests/XcodeSourceGraphTestCase.swift b/Tests/XcodeTests/XcodeSourceGraphTestCase.swift index 43fe207b3..7442172c7 100644 --- a/Tests/XcodeTests/XcodeSourceGraphTestCase.swift +++ b/Tests/XcodeTests/XcodeSourceGraphTestCase.swift @@ -7,9 +7,14 @@ import SystemPackage class XcodeSourceGraphTestCase: SourceGraphTestCase { static func build(projectPath: FilePath) { projectPath.chdir { - let driver = try! XcodeProjectDriver.build(projectPath: projectPath) + let driver = try! XcodeProjectDriver( + projectPath: projectPath, + configuration: configuration, + shell: shell, + logger: logger + ) try! driver.build() - plan = try! driver.plan(logger: Logger().contextualized(with: "index")) + plan = try! driver.plan(logger: logger.contextualized(with: "index")) } } } diff --git a/Tests/XcodeTests/XcodeTargetTest.swift b/Tests/XcodeTests/XcodeTargetTest.swift index af163ac7b..d28a5cd23 100644 --- a/Tests/XcodeTests/XcodeTargetTest.swift +++ b/Tests/XcodeTests/XcodeTargetTest.swift @@ -1,11 +1,32 @@ import Foundation +import Shared @testable import TestShared @testable import XcodeSupport import XCTest final class XcodeTargetTest: XCTestCase { + private var project: XcodeProject! + + override func setUp() { + super.setUp() + let configuration = Configuration() + let logger = Logger(configuration: configuration) + let shell = Shell(logger: logger) + let xcodebuild = Xcodebuild(shell: shell, logger: logger) + project = try! XcodeProject( + path: UIKitProjectPath, + xcodebuild: xcodebuild, + shell: shell, + logger: logger + ) + } + + override func tearDown() { + project = nil + super.tearDown() + } + func testSourceFileInGroupWithoutFolder() throws { - let project = try! XcodeProject(path: UIKitProjectPath) let target = project.targets.first { $0.name == "UIKitProject" }! try target.identifyFiles() @@ -15,7 +36,6 @@ final class XcodeTargetTest: XCTestCase { } func testIsTestTarget() { - let project = try! XcodeProject(path: UIKitProjectPath) let projectTarget = project.targets.first { $0.name == "UIKitProject" }! let testTarget = project.targets.first { $0.name == "UIKitProjectTests" }! diff --git a/Tests/XcodeTests/XcodebuildBuildProjectTest.swift b/Tests/XcodeTests/XcodebuildBuildProjectTest.swift index 29ffd7497..5166e6b1c 100644 --- a/Tests/XcodeTests/XcodebuildBuildProjectTest.swift +++ b/Tests/XcodeTests/XcodebuildBuildProjectTest.swift @@ -10,8 +10,11 @@ final class XcodebuildBuildProjectTest: XCTestCase { override func setUp() { super.setUp() - xcodebuild = Xcodebuild(shell: .shared) - project = try! XcodeProject(path: UIKitProjectPath) + let configuration = Configuration() + let logger = Logger(configuration: configuration) + let shell = Shell(logger: logger) + xcodebuild = Xcodebuild(shell: shell, logger: logger) + project = try! XcodeProject(path: UIKitProjectPath, xcodebuild: xcodebuild, shell: shell, logger: logger) } override func tearDown() { diff --git a/Tests/XcodeTests/XcodebuildSchemesTest.swift b/Tests/XcodeTests/XcodebuildSchemesTest.swift index 72ce5927b..3a7821b8b 100644 --- a/Tests/XcodeTests/XcodebuildSchemesTest.swift +++ b/Tests/XcodeTests/XcodebuildSchemesTest.swift @@ -1,4 +1,5 @@ import Foundation +import Shared @testable import XcodeSupport import XCTest @@ -11,8 +12,10 @@ final class XcodebuildSchemesTest: XCTestCase { super.setUp() shell = ShellMock() - xcodebuild = Xcodebuild(shell: shell) - project = try! XcodeProject(path: UIKitProjectPath) + let configuration = Configuration() + let logger = Logger(configuration: configuration) + xcodebuild = Xcodebuild(shell: shell, logger: logger) + project = try! XcodeProject(path: UIKitProjectPath, xcodebuild: xcodebuild, shell: shell, logger: logger) } override func tearDown() {