Skip to content

Commit fe61816

Browse files
authored
Merge pull request #23 from xing/support_products
Support to use Product as target in SPM
2 parents f178a39 + 41751ec commit fe61816

File tree

9 files changed

+121
-23
lines changed

9 files changed

+121
-23
lines changed

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ ARGUMENTS:
5959
OPTIONS:
6060
--since <since> Equivalent to git-log --since: Eg: '6 months ago' (default: 6 months ago)
6161
--module <module> The Module to compare. If you specify something, target parameter will be ommited
62-
--target <target> The target in your Podfile file to be used
62+
--target <target> The target in your Podfile or Package.swift file to be used (this can be a Product name in SPM)
6363
--output-format <output-format>
6464
csv or json (default: csv)
6565
--version Show the version.
@@ -90,7 +90,7 @@ ARGUMENTS:
9090
OPTIONS:
9191
--to <git-object> The git objects to compare the current graph to. Eg: - 'main', 'my_branch', 'some_commit_hash'. (default: HEAD, main, master)
9292
--module <module> The Module to compare. If you specify something, target parameter will be ommited
93-
--target <target> The target in your Podfile or Package.swift file to be used
93+
--target <target> The target in your Podfile or Package.swift file to be used (this can be a Product name in SPM)
9494
--version Show the version.
9595
-h, --help Show help information.
9696
```
@@ -127,7 +127,7 @@ ARGUMENTS:
127127
<directory-path> Path to the directory where Podfile.lock or Package.swift is located (default: .)
128128

129129
OPTIONS:
130-
--target <target> The target in your Podfile or Package.swift file to be used
130+
--target <target> The target in your Podfile or Package.swift file to be used (this can be a Product name in SPM)
131131
--version Show the version.
132132
-h, --help Show help information.
133133

@@ -157,7 +157,7 @@ ARGUMENTS:
157157
<directory-path> Path to the directory where Podfile.lock or Package.swift is located (default: .)
158158

159159
OPTIONS:
160-
--target <target> The target in your Podfile or Package.swift file to be used
160+
--target <target> The target in your Podfile or Package.swift file to be used (this can be a Product name in SPM)
161161
--show-only-tests Show only Test targets
162162
--version Show the version.
163163
-h, --help Show help information.
@@ -185,7 +185,7 @@ ARGUMENTS:
185185
OPTIONS:
186186
--of <git-object> A git object representing the version to draw the graph for. Eg: - 'main', 'my_branch', 'some_commit_hash'.
187187
--module <module> The Module to compare. If you specify something, target parameter will be ommited
188-
--target <target> The target in your Podfile or Package.swift file to be used
188+
--target <target> The target in your Podfile or Package.swift file to be used (this can be a Product name in SPM)
189189
--use-multiedge Use multi-edge or unique-edge configuration
190190
--show-externals Show Externals modules dependencies
191191
--version Show the version.

Sources/SPMExtractor/Module+Package.swift

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,12 @@ import Shell
44

55
public struct Package: Decodable {
66
public let targets: [Target]
7+
public let products: [Product]
78

9+
public struct Product: Decodable {
10+
let name: String
11+
let targets: [String]
12+
}
813
public struct Target: Decodable {
914
let name: String
1015
let targetDependencies: [String]?
@@ -48,7 +53,7 @@ extension TargetError: CustomStringConvertible {
4853
}
4954
}
5055

51-
public func extracPackageModules(from packageRaw: String, target: String) throws -> ([Module], [String]) {
56+
public func extracPackageModules(from packageRaw: String, target: String, isRootTarget: Bool = true) throws -> ([Module], [String]) {
5257

5358
guard
5459
let data = packageRaw.data(using: .utf8)
@@ -58,15 +63,18 @@ public func extracPackageModules(from packageRaw: String, target: String) throws
5863

5964
let package = try JSONDecoder().decode(Package.self, from: data)
6065

61-
guard let targetModules = package.targets.filter({ $0.name == target }).first else {
66+
if let targetModules = package.targets.filter({ $0.name == target }).first {
67+
let dependencies = extractDependencies(from: package, on: target)
68+
let external = targetModules.productDependencies?.compactMap { Module(name: $0, dependencies: []) } ?? []
69+
let targetDependencies = targetModules.dependencies
70+
return (dependencies + external, targetDependencies)
71+
} else if let product = package.products.filter({ $0.name == target }).first, isRootTarget == true {
72+
let result = try product.targets.compactMap { try extracPackageModules(from: packageRaw, target: $0, isRootTarget: false)}
73+
let modules = Set(result.flatMap(\.0))
74+
return (Array(modules), product.targets)
75+
} else {
6276
throw TargetError.targetNotFound(target: target)
6377
}
64-
65-
let dependencies = extractDependencies(from: package, on: target)
66-
let external = targetModules.productDependencies?.compactMap { Module(name: $0, dependencies: []) } ?? []
67-
68-
let targetDependencies = targetModules.dependencies
69-
return (dependencies + external, targetDependencies)
7078
}
7179

7280
public func extractDependantTargets(from packageRaw: String, target: String) throws -> [Module] {

Sources/jungle/Commands/CompareCommand.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ struct CompareCommand: ParsableCommand {
3535
@Option(help: "The Module to compare. If you specify something, target parameter will be ommited")
3636
var module: String?
3737

38-
@Option(help: "The target in your Podfile or Package.swift file to be used")
38+
@Option(help: "The target in your Podfile or Package.swift file to be used (this can be a Product name in SPM)")
3939
var target: String
4040

4141
@Flag(help: "Use multi-edge or unique-edge configuration")

Sources/jungle/Commands/DependantCommand.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ struct DependantCommand: ParsableCommand {
1111
abstract: "Outputs a sorted list of targets that depends on the specified one in target"
1212
)
1313

14-
@Option(help: "The target in your Podfile or Package.swift file to be used")
14+
@Option(help: "The target in your Podfile or Package.swift file to be used (this can be a Product name in SPM)")
1515
var target: String
1616

1717
@Flag(help: "Show only Test targets")

Sources/jungle/Commands/GraphCommand.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ struct GraphCommand: ParsableCommand {
2121
@Option(help: "The Module to compare. If you specify something, target parameter will be ommited")
2222
var module: String?
2323

24-
@Option(help: "The target in your Podfile or Package.swift file to be used")
24+
@Option(help: "The target in your Podfile or Package.swift file to be used (this can be a Product name in SPM)")
2525
var target: String
2626

2727
@Flag(help: "Use multi-edge or unique-edge configuration")

Sources/jungle/Commands/HistoryCommand.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ struct HistoryCommand: AsyncParsableCommand {
2424
@Option(help: "The Module to compare. If you specify something, target parameter will be ommited")
2525
var module: String?
2626

27-
@Option(help: "The target in your Podfile file to be used")
27+
@Option(help: "The target in your Podfile or Package.swift file to be used (this can be a Product name in SPM)")
2828
var target: String
2929

3030
@Flag(help: "Use multi-edge or unique-edge configuration")
@@ -84,10 +84,10 @@ struct HistoryCommand: AsyncParsableCommand {
8484
}
8585

8686
public func process(entry: GitLogEntry, target: String, directoryURL: URL) async throws -> HistoryStatsOutput? {
87-
guard let package = try? shell("git show \(entry.revision):Package.swift", at: directoryURL), !package.isEmpty else {
87+
guard let package = try? shell("git show \(entry.revision):./Package.swift", at: directoryURL), !package.isEmpty else {
8888
return nil
8989
}
90-
try shell("git show \(entry.revision):Package.swift > Package.swift.new", at: directoryURL)
90+
try shell("git show \(entry.revision):./Package.swift > Package.swift.new", at: directoryURL)
9191
try shell("mv Package.swift Package.swift.current", at: directoryURL)
9292
try shell("mv Package.swift.new Package.swift", at: directoryURL)
9393
guard
@@ -132,15 +132,15 @@ struct HistoryCommand: AsyncParsableCommand {
132132
group.addTask {
133133

134134
guard
135-
let podfile = try? shell("git show \(entry.revision):Podfile", at: directoryURL),
135+
let podfile = try? shell("git show \(entry.revision):./Podfile", at: directoryURL),
136136
let entryTargetDependencies = try? moduleFromPodfile(podfile, on: target) ?? .init(name: target, dependencies: [])
137137
else {
138138
return nil
139139
}
140140

141141
return try? await entry.process(
142142
pod: module,
143-
podfile: shell("git show \(entry.revision):Podfile.lock", at: directoryURL),
143+
podfile: shell("git show \(entry.revision):./Podfile.lock", at: directoryURL),
144144
target: entryTargetDependencies,
145145
usingMultiEdge: useMultiedge
146146
)

Sources/jungle/Commands/Main.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ struct Jungle: AsyncParsableCommand {
55
static var configuration = CommandConfiguration(
66
commandName: "jungle",
77
abstract: "SwiftPM and Cocoapods based projects complexity analyzer.",
8-
version: "2.2.2",
8+
version: "2.3.2",
99
subcommands: [HistoryCommand.self, CompareCommand.self, GraphCommand.self, ModulesCommand.self, DependantCommand.self],
1010
defaultSubcommand: CompareCommand.self
1111
)

Sources/jungle/Commands/ModulesCommand.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ struct ModulesCommand: ParsableCommand {
1313
abstract: "Outputs a sorted list of modules dependencies count of your project"
1414
)
1515

16-
@Option(help: "The target in your Podfile or Package.swift file to be used")
16+
@Option(help: "The target in your Podfile or Package.swift file to be used (this can be a Product name in SPM)")
1717
var target: String
1818

1919
@Argument(help: "Path to the directory where Podfile.lock or Package.swift is located")

Tests/SPMExtractorTests/SPMExtractorTests.swift

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,4 +452,94 @@ final class SPMExtractorTests: XCTestCase {
452452
let dependant = try extractDependantTargets(from: rawPackage, target: "SamplePackage")
453453
XCTAssertEqual(dependant.map(\.name).sorted(), ["Library", "LibraryTests", "SamplePackageTests"])
454454
}
455+
456+
func testProductModulesFromPackage() throws {
457+
let rawPackage = """
458+
{
459+
"dependencies" : [
460+
{
461+
"identity" : "swift-algorithms",
462+
"requirement" : {
463+
"range" : [
464+
{
465+
"lower_bound" : "1.0.0",
466+
"upper_bound" : "2.0.0"
467+
}
468+
]
469+
},
470+
"type" : "sourceControl",
471+
"url" : "https://github.com/apple/swift-algorithms"
472+
}
473+
],
474+
"manifest_display_name" : "MyPackage",
475+
"name" : "MyPackage",
476+
"path" : "/Users/oswaldo.rubio/Desktop/MyPackage",
477+
"platforms" : [
478+
479+
],
480+
"products" : [
481+
{
482+
"name" : "MyPackage",
483+
"targets" : [
484+
"MyPackage"
485+
],
486+
"type" : {
487+
"library" : [
488+
"automatic"
489+
]
490+
}
491+
},
492+
{
493+
"name" : "FeaturesContainer",
494+
"targets" : [
495+
"MyPackage"
496+
],
497+
"type" : {
498+
"library" : [
499+
"automatic"
500+
]
501+
}
502+
}
503+
],
504+
"targets" : [
505+
{
506+
"c99name" : "MyPackageTests",
507+
"module_type" : "SwiftTarget",
508+
"name" : "MyPackageTests",
509+
"path" : "Tests/MyPackageTests",
510+
"sources" : [
511+
"MyPackageTests.swift"
512+
],
513+
"target_dependencies" : [
514+
"MyPackage"
515+
],
516+
"type" : "test"
517+
},
518+
{
519+
"c99name" : "MyPackage",
520+
"module_type" : "SwiftTarget",
521+
"name" : "MyPackage",
522+
"path" : "Sources/MyPackage",
523+
"product_dependencies" : [
524+
"Algorithms"
525+
],
526+
"product_memberships" : [
527+
"MyPackage",
528+
"FeaturesContainer"
529+
],
530+
"sources" : [
531+
"MyPackage.swift"
532+
],
533+
"type" : "library"
534+
}
535+
],
536+
"tools_version" : "5.8"
537+
}
538+
"""
539+
540+
let (dependencies, targetDependencies) = try extracPackageModules(from: rawPackage, target: "FeaturesContainer")
541+
542+
XCTAssertEqual(dependencies.map(\.name).sorted(), ["Algorithms", "MyPackage"])
543+
XCTAssertEqual(targetDependencies.sorted(), ["MyPackage"])
544+
}
455545
}

0 commit comments

Comments
 (0)