Skip to content

Commit

Permalink
Automatic target detection (#767)
Browse files Browse the repository at this point in the history
  • Loading branch information
ileitch committed Aug 4, 2024
1 parent 12b22b5 commit 1fccca3
Show file tree
Hide file tree
Showing 206 changed files with 431 additions and 954 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -91,4 +91,4 @@ jobs:
- name: Test
run: ${{ env.swift_test }}
- name: Scan
run: ${{ env.periphery_scan }} --config .periphery.linux.yml
run: ${{ env.periphery_scan }}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ DerivedData
*.gcda
*.gcno
.swiftpm
Tests/Fixtures/.build/

# VSCode
.vscode/*
11 changes: 0 additions & 11 deletions .periphery.linux.yml

This file was deleted.

13 changes: 0 additions & 13 deletions .periphery.yml

This file was deleted.

20 changes: 10 additions & 10 deletions Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-argument-parser",
"state" : {
"revision" : "0fbc8848e389af3bb55c182bc19ca9d5dc2f255b",
"version" : "1.4.0"
"revision" : "41982a3656a71c768319979febd796c6fd111d5c",
"version" : "1.5.0"
}
},
{
Expand All @@ -59,35 +59,35 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-syntax",
"state" : {
"revision" : "303e5c5c36d6a558407d364878df131c3546fad8",
"version" : "510.0.2"
"revision" : "2bc86522d115234d1f588efe2bcb4ce4be8f8b82",
"version" : "510.0.3"
}
},
{
"identity" : "swift-system",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-system",
"state" : {
"revision" : "6a9e38e7bd22a3b8ba80bddf395623cf68f57807",
"version" : "1.3.1"
"revision" : "d2ba781702a1d8285419c15ee62fd734a9437ff5",
"version" : "1.3.2"
}
},
{
"identity" : "xcodeproj",
"kind" : "remoteSourceControl",
"location" : "https://github.com/tuist/xcodeproj",
"state" : {
"revision" : "20d5803c70e4ac5d67151ea6f5624a8136ab8fe0",
"version" : "8.21.0"
"revision" : "7713589d4d1bceedd02899d9c44b8e57be05ea35",
"version" : "8.22.0"
}
},
{
"identity" : "yams",
"kind" : "remoteSourceControl",
"location" : "https://github.com/jpsim/Yams",
"state" : {
"revision" : "9234124cff5e22e178988c18d8b95a8ae8007f76",
"version" : "5.1.2"
"revision" : "3036ba9d69cf1fd04d433527bc339dc0dc75433d",
"version" : "5.1.3"
}
}
],
Expand Down
47 changes: 1 addition & 46 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ var dependencies: [Package.Dependency] = [
.package(url: "https://github.com/tadija/AEXML", from: "4.0.0"),
.package(url: "https://github.com/apple/swift-argument-parser", from: "1.0.0"),
.package(url: "https://github.com/ileitch/swift-indexstore", from: "9.0.4"),
.package(url: "https://github.com/apple/swift-syntax", from: "510.0.2"),
.package(url: "https://github.com/apple/swift-syntax", from: "510.0.3"),
.package(url: "https://github.com/ileitch/swift-filename-matcher", from: "0.0.0")
]

Expand Down Expand Up @@ -89,43 +89,6 @@ var targets: [PackageDescription.Target] = [
],
path: "Tests/Shared"
),
.target(
name: "ExternalModuleFixtures",
path: "Tests/Fixtures/ExternalModuleFixtures"
),
.target(
name: "CrossModuleRetentionFixtures",
dependencies: [
.target(name: "CrossModuleRetentionSupportFixtures")
],
path: "Tests/Fixtures/CrossModuleRetentionFixtures"
),
.target(
name: "CrossModuleRetentionSupportFixtures",
path: "Tests/Fixtures/CrossModuleRetentionSupportFixtures"
),
.target(
name: "RetentionFixtures",
dependencies: [
.target(name: "ExternalModuleFixtures")
],
path: "Tests/Fixtures/RetentionFixtures"
),
.target(
name: "UnusedParameterFixtures",
path: "Tests/Fixtures/UnusedParameterFixtures",
swiftSettings: [
.unsafeFlags(["-suppress-warnings"]) // Suppress warnings from testLocalVariableAssignment
]
),
.target(
name: "TypeSyntaxInspectorFixtures",
path: "Tests/Fixtures/TypeSyntaxInspectorFixtures"
),
.target(
name: "DeclarationVisitorFixtures",
path: "Tests/Fixtures/DeclarationVisitorFixtures"
),
.testTarget(
name: "PeripheryTests",
dependencies: [
Expand Down Expand Up @@ -162,14 +125,6 @@ targets.append(contentsOf: [
.product(name: "XcodeProj", package: "XcodeProj")
]
),
.target(
name: "ObjcAccessibleRetentionFixtures",
path: "Tests/Fixtures/ObjcAccessibleRetentionFixtures"
),
.target(
name: "ObjcAnnotatedRetentionFixtures",
path: "Tests/Fixtures/ObjcAnnotatedRetentionFixtures"
),
.testTarget(
name: "XcodeTests",
dependencies: [
Expand Down
18 changes: 0 additions & 18 deletions Sources/Frontend/Commands/ScanBehavior.swift
Original file line number Diff line number Diff line change
Expand Up @@ -90,24 +90,6 @@ final class ScanBehavior {
logger.info(output, canQuiet: false)
logger.endInterval(interval)

if filteredResults.count > 0,
configuration.outputFormat.supportsAuxiliaryOutput {
logger.info(
colorize("\n* ", .boldGreen) +
colorize("Seeing false positives?", .bold) +

colorize("\n - ", .boldGreen) +
"Periphery only analyzes files that are members of the targets you specify." +
"\n References to declarations identified as unused may reside in files that are members of other targets, e.g test targets." +

colorize("\n - ", .boldGreen) +
"Periphery is a very precise tool, false positives often turn out to be correct after further investigation." +

colorize("\n - ", .boldGreen) +
"If it really is a false positive, please report it - https://github.com/peripheryapp/periphery/issues."
)
}

updateChecker.notifyIfAvailable()

if !filteredResults.isEmpty && configuration.strict {
Expand Down
17 changes: 2 additions & 15 deletions Sources/Frontend/Commands/ScanCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,15 @@ struct ScanCommand: FrontendCommand {
@Option(help: "Path to configuration file. By default Periphery will look for .periphery.yml in the current directory")
var config: String?

@Option(help: "Path to your project's .xcworkspace. Xcode projects only")
var workspace: String?

@Option(help: "Path to your project's .xcodeproj - supply this option if your project doesn't have an .xcworkspace. Xcode projects only")
@Option(help: "Path to your project's .xcodeproj or .xcworkspace")
var project: String?

@Option(parsing: .upToNextOption, help: "File target mapping configuration file paths. For use with third-party build systems")
var fileTargetsPath: [FilePath] = defaultConfiguration.$fileTargetsPath.defaultValue

@Option(parsing: .upToNextOption, help: "Schemes that must be built in order to produce the targets passed to the --targets option. Xcode projects only")
@Option(parsing: .upToNextOption, help: "Schemes to build. All targets built by these schemes will be scanned")
var schemes: [String] = defaultConfiguration.$schemes.defaultValue

@Option(parsing: .upToNextOption, help: "Target names to scan. Required for Xcode projects. Optional for Swift Package Manager projects, default behavior is to scan all targets defined in Package.swift")
var targets: [String] = defaultConfiguration.$targets.defaultValue

@Option(help: "Output format (allowed: \(OutputFormat.allValueStrings.joined(separator: ", ")))")
var format: OutputFormat = defaultConfiguration.$outputFormat.defaultValue

Expand Down Expand Up @@ -117,9 +111,6 @@ struct ScanCommand: FrontendCommand {
@Flag(help: "Only output results")
var quiet: Bool = defaultConfiguration.$quiet.defaultValue

@Option(help: "JSON package manifest path (obtained using `swift package describe --type json` or manually)")
var jsonPackageManifestPath: String?

@Option(help: "Baseline file path used to filter results")
var baseline: FilePath?

Expand All @@ -137,11 +128,9 @@ struct ScanCommand: FrontendCommand {

let configuration = Configuration.shared
configuration.guidedSetup = setup
configuration.apply(\.$workspace, workspace)
configuration.apply(\.$project, project)
configuration.apply(\.$fileTargetsPath, fileTargetsPath)
configuration.apply(\.$schemes, schemes)
configuration.apply(\.$targets, targets)
configuration.apply(\.$indexExclude, indexExclude)
configuration.apply(\.$reportExclude, reportExclude)
configuration.apply(\.$reportInclude, reportInclude)
Expand Down Expand Up @@ -171,10 +160,8 @@ struct ScanCommand: FrontendCommand {
configuration.apply(\.$relativeResults, relativeResults)
configuration.apply(\.$retainCodableProperties, retainCodableProperties)
configuration.apply(\.$retainEncodableProperties, retainEncodableProperties)
configuration.apply(\.$jsonPackageManifestPath, jsonPackageManifestPath)
configuration.apply(\.$baseline, baseline)
configuration.apply(\.$writeBaseline, writeBaseline)

try scanBehavior.main { project in
try Scan().perform(project: project)
}.get()
Expand Down
4 changes: 2 additions & 2 deletions Sources/Frontend/CommonSetupGuide.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ final class CommonSetupGuide: SetupGuideHelpers, SetupGuide {
}

func perform() throws {
print(colorize("\nAssume all 'public' declarations are in use?", .bold))
print(colorize("?", .boldYellow) + " You should choose 'Yes' here if your public interfaces are not used by any selected build target, as may be the case for a framework/library project.")
print(colorize("Assume all 'public' declarations are in use?", .bold))
print(colorize("?", .boldYellow) + " Choose 'Yes' if your project is a framework/library without a main application target.")
configuration.retainPublic = selectBoolean()
}

Expand Down
11 changes: 7 additions & 4 deletions Sources/Frontend/GuidedSetup.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,15 @@ final class GuidedSetup: SetupGuideHelpers {
let guides: [SetupGuide] = [projectGuide, commonGuide]
try guides.forEach { try $0.perform() }
let options = Array(guides.map { $0.commandLineOptions }.joined())
var shouldSave = false

print(colorize("\nSave configuration to \(Configuration.defaultConfigurationFile)?", .bold))
let shouldSave = selectBoolean()
if configuration.hasNonDefaultValues {
print(colorize("\nSave configuration to \(Configuration.defaultConfigurationFile)?", .bold))
shouldSave = selectBoolean()

if shouldSave {
try configuration.save()
if shouldSave {
try configuration.save()
}
}

print(colorize("\n*", .boldGreen) + " Executing command:")
Expand Down
2 changes: 1 addition & 1 deletion Sources/Frontend/Project.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ final class Project {
static func identify() -> Self {
let configuration = Configuration.shared

if configuration.workspace != nil || configuration.project != nil {
if configuration.project != nil {
return self.init(kind: .xcode)
} else if !configuration.fileTargetsPath.isEmpty {
return self.init(kind: .generic)
Expand Down
39 changes: 2 additions & 37 deletions Sources/Frontend/SPMProjectSetupGuide.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,6 @@ import PeripheryKit
import Shared

final class SPMProjectSetupGuide: SetupGuideHelpers, ProjectSetupGuide {
private let configuration: Configuration

init(configuration: Configuration = .shared) {
self.configuration = configuration
super.init()
}

var projectKind: ProjectKind {
.spm
}
Expand All @@ -19,37 +12,9 @@ final class SPMProjectSetupGuide: SetupGuideHelpers, ProjectSetupGuide {
SPM.isSupported
}

func perform() throws {
let package = try SPM.Package.load()
let selection = try selectTargets(in: package)

if case let .some(targets) = selection {
configuration.targets = targets
}
}
func perform() {}

var commandLineOptions: [String] {
var options: [String] = []

if !configuration.targets.isEmpty {
options.append("--targets " + configuration.targets.map { "\"\($0)\"" }.joined(separator: ","))
}

return options
[]
}

// MARK: - Private

private func selectTargets(in package: SPM.Package) throws -> SetupSelection {
let targets = package.swiftTargets

guard !targets.isEmpty else {
throw PeripheryError.guidedSetupError(message: "Failed to identify any targets in package \(package.name)")
}

print(colorize("Select build targets to analyze:", .bold))
let targetNames = targets.map { $0.name }.sorted()
return select(multiple: targetNames, allowAll: true)
}

}
5 changes: 4 additions & 1 deletion Sources/Frontend/Scan.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,11 @@ final class Scan {
logger.info("\(asterisk) Indexing...")
}

let indexLogger = logger.contextualized(with: "index")
let sourceFiles = try driver.collect(logger: indexLogger)

let graph = SourceGraph.shared
try driver.index(graph: graph)
try driver.index(sourceFiles: sourceFiles, graph: graph, logger: indexLogger)
logger.endInterval(indexInterval)

let analyzeInterval = logger.beginInterval("analyze")
Expand Down
9 changes: 0 additions & 9 deletions Sources/Indexer/IndexTarget.swift

This file was deleted.

5 changes: 5 additions & 0 deletions Sources/Indexer/Indexer.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Foundation
import SystemPackage
import Shared
import SourceGraph
import FilenameMatcher

public class Indexer {
Expand All @@ -16,4 +17,8 @@ public class Indexer {
let included = files.filter { !configuration.indexExcludeMatchers.anyMatch(filename: $0.string) }
return (included, files.subtracting(included))
}

func isRetained(_ file: SourceFile) -> Bool {
configuration.retainFilesMatchers.anyMatch(filename: file.path.string)
}
}
Loading

0 comments on commit 1fccca3

Please sign in to comment.