Skip to content

Commit 1e18b87

Browse files
Merge pull request #9 from Nikoloutsos/feature/rename-deriveddatapath-and-make-it-work
Fix --derivedDataPath not working properly and renaming CLI option to --dataStorePath
2 parents f120d5c + 668ee8e commit 1e18b87

17 files changed

Lines changed: 96 additions & 138 deletions

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ brew install swiftfindrefs
3838
## ⚙️ Command line options
3939

4040
### Common options (available for all subcommands)
41-
- `-p, --projectName` helps the tool infer the right DerivedData folder when you do not pass `derivedDataPath`.
42-
- `-d, --derivedDataPath` points directly to a DerivedData (or IndexStoreDB) directory and skips discovery.
41+
- `-p, --projectName` helps the tool infer the right DerivedData folder when `dataStorePath` is not provided.
42+
- `-d, --dataStorePath` points directly to a DataStore directory. When provided, this takes priority over `--projectName`.
4343
- `-v, --verbose` enables verbose output for diagnostic purposes (flag, no value required).
4444

4545
### Search subcommand

Sources/SwiftFindRefs/CompositionRoot/RemoveCompositionRoot.swift

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import Foundation
33

44
struct RemoveCompositionRoot {
55
let projectName: String?
6-
let derivedDataPath: String?
6+
let dataStorePath: String?
77
let excludeCompilationConditionals: Bool
88
let print: (String) -> Void
99
let vPrint: (String) -> Void
@@ -12,14 +12,20 @@ struct RemoveCompositionRoot {
1212
let removerFactory: (String) -> UnnecessaryTestableRemoving
1313

1414
func run() async throws {
15-
let derivedDataPaths = try derivedDataLocator.locateDerivedData(
16-
projectName: projectName,
17-
derivedDataPath: derivedDataPath
18-
)
19-
vPrint("DerivedData path: \(derivedDataPaths.derivedDataURL.path)")
20-
vPrint("IndexStoreDB path: \(derivedDataPaths.indexStoreDBURL.path)")
21-
let indexStorePath = derivedDataPaths.indexStoreDBURL.deletingLastPathComponent().path
22-
let remover = removerFactory(indexStorePath)
15+
var pathToDataStore: String
16+
if let dataStorePath {
17+
guard fileSystem.fileExists(atPath: dataStorePath) else { throw DataStorePathValidationError.invalidPath(dataStorePath) }
18+
pathToDataStore = dataStorePath
19+
} else {
20+
let derivedDataPaths = try derivedDataLocator.locateDerivedData(
21+
projectName: projectName
22+
)
23+
pathToDataStore = derivedDataPaths.dataStoreURL.path()
24+
vPrint("Discovering DataStore path based on projectName: \(String(describing: projectName))")
25+
}
26+
vPrint("Using DataStore path: \(pathToDataStore)")
27+
28+
let remover = removerFactory(pathToDataStore)
2329
let updatedFiles = try await remover.run()
2430
print("✅ Updated \(updatedFiles.count) files")
2531
vPrint("Updated files:")

Sources/SwiftFindRefs/CompositionRoot/SearchCompositionRoot.swift

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import Foundation
22

33
struct SearchCompositionRoot {
44
let projectName: String?
5-
let derivedDataPath: String?
5+
let dataStorePath: String?
66
let symbolName: String
77
let symbolType: String?
88
let print: (String) -> Void
@@ -11,14 +11,19 @@ struct SearchCompositionRoot {
1111
let derivedDataLocator: DerivedDataLocatorProtocol
1212

1313
func run() async throws {
14-
let derivedDataPaths = try derivedDataLocator.locateDerivedData(
15-
projectName: projectName,
16-
derivedDataPath: derivedDataPath
17-
)
18-
vPrint("DerivedData path: \(derivedDataPaths.derivedDataURL.path)")
19-
vPrint("IndexStoreDB path: \(derivedDataPaths.indexStoreDBURL.path)")
20-
let indexStorePath = derivedDataPaths.indexStoreDBURL.deletingLastPathComponent().path
21-
let indexStoreFinder = IndexStoreFinder(indexStorePath: indexStorePath)
14+
var pathToDataStore: String
15+
if let dataStorePath {
16+
guard fileSystem.fileExists(atPath: dataStorePath) else { throw DataStorePathValidationError.invalidPath(dataStorePath) }
17+
pathToDataStore = dataStorePath
18+
19+
} else {
20+
let derivedDataPaths = try derivedDataLocator.locateDerivedData(projectName: projectName)
21+
pathToDataStore = derivedDataPaths.dataStoreURL.path()
22+
vPrint("Discovering DataStore path based on projectName: \(String(describing: projectName))")
23+
}
24+
vPrint("Using DataStore path: \(pathToDataStore)")
25+
26+
let indexStoreFinder = IndexStoreFinder(indexStorePath: pathToDataStore)
2227
print("🔍 Searching for references to symbol '\(symbolName)' of type '\(symbolType ?? "any")'")
2328
let references = try await indexStoreFinder.fileReferences(of: symbolName, symbolType: symbolType)
2429
print("✅ Found \(references.count) references:\n\(references.joined(separator: "\n"))")
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import Foundation
2+
3+
/// Errors thrown while validating the DataPath for a project.
4+
enum DataStorePathValidationError: LocalizedError {
5+
case invalidPath(String)
6+
7+
/// Human-readable description surfaced to end users.
8+
var errorDescription: String? {
9+
switch self {
10+
case .invalidPath(let path):
11+
"❌ The provided DataStore path does not exist: \(path)."
12+
}
13+
}
14+
}

Sources/SwiftFindRefs/DerivedData/DerivedDataLocator.swift

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,20 +21,12 @@ struct DerivedDataLocator: DerivedDataLocatorProtocol {
2121
/// Finds the DerivedData directory for the given inputs and indicates whether helper paths should be appended.
2222
/// - Parameters:
2323
/// - projectName: The name of the Xcode project whose DerivedData should be inferred when no explicit path is supplied.
24-
/// - derivedDataPath: An optional explicit DerivedData path which, when present, takes precedence over project resolution.
24+
/// - dataStorePath: An optional explicit DataStore path which, when present, takes precedence over project resolution.
2525
/// - Returns: A ``DerivedDataPaths`` value containing the resolved URL and whether additional IndexStore paths should be appended.
2626
/// - Throws: ``DerivedDataLocatorError`` when inputs are missing, invalid, or when the DerivedData directory cannot be found.
2727
func locateDerivedData(
28-
projectName: String?,
29-
derivedDataPath: String?
28+
projectName: String?
3029
) throws -> DerivedDataPaths {
31-
if let derivedDataPath, !derivedDataPath.isEmpty {
32-
guard fileSystem.fileExists(atPath: derivedDataPath) else {
33-
throw DerivedDataLocatorError.invalidPath(derivedDataPath)
34-
}
35-
return DerivedDataPaths(derivedDataURL: URL(fileURLWithPath: derivedDataPath), shouldAppendExtraPaths: false)
36-
}
37-
3830
guard let projectName, !projectName.isEmpty else {
3931
throw DerivedDataLocatorError.missingInputs
4032
}

Sources/SwiftFindRefs/DerivedData/DerivedDataLocatorError.swift

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,22 @@ import Foundation
22

33
/// Errors thrown while locating the DerivedData directory for a project.
44
enum DerivedDataLocatorError: LocalizedError {
5-
/// No usable `projectName` or `derivedDataPath` was provided.
5+
/// No usable `projectName` or `dataStorePath` was provided.
66
case missingInputs
77
/// The default DerivedData root directory does not exist at the expected path.
88
case derivedDataRootMissing(String)
99
/// A DerivedData entry matching the provided project name was not found.
1010
case projectNotFound(String)
11-
/// The explicit DerivedData path provided by the caller could not be resolved on disk.
12-
case invalidPath(String)
1311

1412
/// Human-readable description surfaced to end users.
1513
var errorDescription: String? {
1614
switch self {
1715
case .missingInputs:
18-
"❌ Either projectName or derivedDataPath must be provided."
16+
"❌ Either projectName or dataStorePath must be provided."
1917
case .derivedDataRootMissing(let path):
2018
"❌ DerivedData root directory was not found at \(path)."
2119
case .projectNotFound(let name):
2220
"❌ No DerivedData entry matching project \(name) was found."
23-
case .invalidPath(let path):
24-
"❌ The provided DerivedData path does not exist: \(path)."
2521
}
2622
}
2723
}

Sources/SwiftFindRefs/DerivedData/DerivedDataLocatorProtocol.swift

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,10 @@ protocol DerivedDataLocatorProtocol {
55
/// Locates the DerivedData directory either from an explicit path or by inferring it from the project name.
66
/// - Parameters:
77
/// - projectName: The name of the Xcode project whose DerivedData should be searched.
8-
/// - derivedDataPath: An optional explicit DerivedData path that takes precedence when provided.
8+
/// - dataStorePath: An optional explicit DataStore path that takes precedence when provided.
99
/// - Returns: Paths describing the resolved DerivedData location and whether helper paths should be appended.
1010
/// - Throws: ``DerivedDataLocatorError`` when the provided inputs are invalid or the DerivedData directory cannot be found.
1111
func locateDerivedData(
12-
projectName: String?,
13-
derivedDataPath: String?
12+
projectName: String?
1413
) throws -> DerivedDataPaths
1514
}

Sources/SwiftFindRefs/DerivedData/DerivedDataPaths.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,10 @@ struct DerivedDataPaths {
88
let shouldAppendExtraPaths: Bool
99

1010
/// Points to the IndexStore database, automatically appending the Xcode-specific subpath when needed.
11-
var indexStoreDBURL: URL {
11+
var dataStoreURL: URL {
1212
shouldAppendExtraPaths ? derivedDataURL
1313
.appendingPathComponent("Index.noindex", isDirectory: true)
1414
.appendingPathComponent("DataStore", isDirectory: true)
15-
.appendingPathComponent("IndexStoreDB", isDirectory: true)
1615
: derivedDataURL
1716
}
1817
}

Sources/SwiftFindRefs/SwiftFindRefs.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,16 @@ extension SwiftFindRefs {
1717
@Option(name: [.short, .customLong("projectName")], help: "The name of the Xcode project to help CLI find the Derived Data Index Store Path")
1818
var projectName: String?
1919

20-
@Option(name: [.short, .customLong("derivedDataPath")], help: "The Derived Data path where Xcode stores build data")
21-
var derivedDataPath: String?
20+
@Option(name: [.short, .customLong("dataStorePath")], help: "The DataStore path inside DerivedData where Xcode stores index data")
21+
var dataStorePath: String?
2222

2323
/// Flag to enable verbose output for diagnostic purposes.
2424
@Flag(name: .shortAndLong, help: "Enable verbose output.")
2525
var verbose: Bool = false
2626

2727
func validate() throws {
28-
guard projectName?.isEmpty == false || derivedDataPath?.isEmpty == false else {
29-
throw ValidationError("Provide either --projectName or --derivedDataPath.")
28+
guard projectName?.isEmpty == false || dataStorePath?.isEmpty == false else {
29+
throw ValidationError("Provide either --projectName or --dataStorePath.")
3030
}
3131
}
3232
}
@@ -48,7 +48,7 @@ extension SwiftFindRefs {
4848
let derivedDataLocator = DerivedDataLocator(fileSystem: fileSystem)
4949
let compositionRoot = SearchCompositionRoot(
5050
projectName: common.projectName,
51-
derivedDataPath: common.derivedDataPath,
51+
dataStorePath: common.dataStorePath,
5252
symbolName: name,
5353
symbolType: type,
5454
print: { print($0) },
@@ -79,7 +79,7 @@ extension SwiftFindRefs {
7979
let derivedDataLocator = DerivedDataLocator(fileSystem: fileSystem)
8080
let compositionRoot = RemoveCompositionRoot(
8181
projectName: common.projectName,
82-
derivedDataPath: common.derivedDataPath,
82+
dataStorePath: common.dataStorePath,
8383
excludeCompilationConditionals: excludeCompilationConditionals,
8484
print: { print($0) },
8585
vPrint: { if common.verbose { print($0) } },

Tests/SwiftFindRefs/CompositionRoot/RemoveCompositionRootTests.swift

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,14 @@ struct RemoveCompositionRootTests {
1010
let fileSystem = MockFileSystem(fileExistsResults: [invalidPath: false])
1111
let sut = makeRemoveSUT(
1212
projectName: "Project",
13-
derivedDataPath: invalidPath,
13+
dataStorePath: invalidPath,
1414
excludeCompilationConditionals: false,
1515
fileSystem: fileSystem,
1616
removerFactory: { _ in MockRemover(result: []) }
1717
)
1818

1919
// When
20-
let error = await #expect(throws: DerivedDataLocatorError.self) {
20+
let error = await #expect(throws: DataStorePathValidationError.self) {
2121
try await sut.run()
2222
}
2323

@@ -32,12 +32,12 @@ struct RemoveCompositionRootTests {
3232
@Test("test run prints updated files count when nothing updated")
3333
func test_run_WhenNoUpdates_PrintsUpdatedCount() async throws {
3434
// Given
35-
let derivedDataPath = "/mock/DerivedData/IndexStoreDB"
36-
let fileSystem = MockFileSystem(fileExistsResults: [derivedDataPath: true])
35+
let dataStorePath = "/mock/DerivedData/IndexStoreDB"
36+
let fileSystem = MockFileSystem(fileExistsResults: [dataStorePath: true])
3737
var printMessages: [String] = []
3838
let sut = makeRemoveSUT(
3939
projectName: "Project",
40-
derivedDataPath: derivedDataPath,
40+
dataStorePath: dataStorePath,
4141
excludeCompilationConditionals: false,
4242
fileSystem: fileSystem,
4343
print: { printMessages.append($0) },
@@ -54,15 +54,15 @@ struct RemoveCompositionRootTests {
5454
@Test("test run prints updated files when remover returns results")
5555
func test_run_WhenUpdatesExist_PrintsUpdatedFiles() async throws {
5656
// Given
57-
let derivedDataPath = "/mock/DerivedData/IndexStoreDB"
58-
let fileSystem = MockFileSystem(fileExistsResults: [derivedDataPath: true])
57+
let dataStorePath = "/mock/DerivedData/IndexStoreDB"
58+
let fileSystem = MockFileSystem(fileExistsResults: [dataStorePath: true])
5959
var printMessages: [String] = []
6060
var vPrintMessages: [String] = []
6161
let updatedFiles = ["/mock/FileA.swift", "/mock/FileB.swift"]
6262
var receivedIndexStorePath: String?
6363
let sut = makeRemoveSUT(
6464
projectName: "Project",
65-
derivedDataPath: derivedDataPath,
65+
dataStorePath: dataStorePath,
6666
excludeCompilationConditionals: false,
6767
fileSystem: fileSystem,
6868
print: { printMessages.append($0) },
@@ -77,7 +77,7 @@ struct RemoveCompositionRootTests {
7777
try await sut.run()
7878

7979
// Then
80-
#expect(receivedIndexStorePath == "/mock/DerivedData")
80+
#expect(receivedIndexStorePath == "/mock/DerivedData/IndexStoreDB")
8181
#expect(printMessages.contains("✅ Updated 2 files"))
8282
#expect(vPrintMessages.contains("Updated files:"))
8383
#expect(vPrintMessages.contains("/mock/FileA.swift"))
@@ -86,7 +86,7 @@ struct RemoveCompositionRootTests {
8686

8787
private func makeRemoveSUT(
8888
projectName: String?,
89-
derivedDataPath: String?,
89+
dataStorePath: String?,
9090
excludeCompilationConditionals: Bool,
9191
fileSystem: MockFileSystem,
9292
print: @escaping (String) -> Void = { _ in },
@@ -95,7 +95,7 @@ struct RemoveCompositionRootTests {
9595
) -> RemoveCompositionRoot {
9696
RemoveCompositionRoot(
9797
projectName: projectName,
98-
derivedDataPath: derivedDataPath,
98+
dataStorePath: dataStorePath,
9999
excludeCompilationConditionals: excludeCompilationConditionals,
100100
print: print,
101101
vPrint: vPrint,

0 commit comments

Comments
 (0)