Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support to use Product as target in SPM #23

Merged
merged 5 commits into from
Aug 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ ARGUMENTS:
OPTIONS:
--since <since> Equivalent to git-log --since: Eg: '6 months ago' (default: 6 months ago)
--module <module> The Module to compare. If you specify something, target parameter will be ommited
--target <target> The target in your Podfile file to be used
--target <target> The target in your Podfile or Package.swift file to be used (this can be a Product name in SPM)
--output-format <output-format>
csv or json (default: csv)
--version Show the version.
Expand Down Expand Up @@ -90,7 +90,7 @@ ARGUMENTS:
OPTIONS:
--to <git-object> The git objects to compare the current graph to. Eg: - 'main', 'my_branch', 'some_commit_hash'. (default: HEAD, main, master)
--module <module> The Module to compare. If you specify something, target parameter will be ommited
--target <target> The target in your Podfile or Package.swift file to be used
--target <target> The target in your Podfile or Package.swift file to be used (this can be a Product name in SPM)
--version Show the version.
-h, --help Show help information.
```
Expand Down Expand Up @@ -127,7 +127,7 @@ ARGUMENTS:
<directory-path> Path to the directory where Podfile.lock or Package.swift is located (default: .)

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

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

OPTIONS:
--target <target> The target in your Podfile or Package.swift file to be used
--target <target> The target in your Podfile or Package.swift file to be used (this can be a Product name in SPM)
--show-only-tests Show only Test targets
--version Show the version.
-h, --help Show help information.
Expand Down Expand Up @@ -185,7 +185,7 @@ ARGUMENTS:
OPTIONS:
--of <git-object> A git object representing the version to draw the graph for. Eg: - 'main', 'my_branch', 'some_commit_hash'.
--module <module> The Module to compare. If you specify something, target parameter will be ommited
--target <target> The target in your Podfile or Package.swift file to be used
--target <target> The target in your Podfile or Package.swift file to be used (this can be a Product name in SPM)
--use-multiedge Use multi-edge or unique-edge configuration
--show-externals Show Externals modules dependencies
--version Show the version.
Expand Down
24 changes: 16 additions & 8 deletions Sources/SPMExtractor/Module+Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@ import Shell

public struct Package: Decodable {
public let targets: [Target]
public let products: [Product]

public struct Product: Decodable {
let name: String
let targets: [String]
}
public struct Target: Decodable {
let name: String
let targetDependencies: [String]?
Expand Down Expand Up @@ -48,7 +53,7 @@ extension TargetError: CustomStringConvertible {
}
}

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

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

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

guard let targetModules = package.targets.filter({ $0.name == target }).first else {
if let targetModules = package.targets.filter({ $0.name == target }).first {
let dependencies = extractDependencies(from: package, on: target)
let external = targetModules.productDependencies?.compactMap { Module(name: $0, dependencies: []) } ?? []
let targetDependencies = targetModules.dependencies
return (dependencies + external, targetDependencies)
} else if let product = package.products.filter({ $0.name == target }).first, isRootTarget == true {
let result = try product.targets.compactMap { try extracPackageModules(from: packageRaw, target: $0, isRootTarget: false)}
let modules = Set(result.flatMap(\.0))
return (Array(modules), product.targets)
} else {
throw TargetError.targetNotFound(target: target)
}

let dependencies = extractDependencies(from: package, on: target)
let external = targetModules.productDependencies?.compactMap { Module(name: $0, dependencies: []) } ?? []

let targetDependencies = targetModules.dependencies
return (dependencies + external, targetDependencies)
}

public func extractDependantTargets(from packageRaw: String, target: String) throws -> [Module] {
Expand Down
2 changes: 1 addition & 1 deletion Sources/jungle/Commands/CompareCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ struct CompareCommand: ParsableCommand {
@Option(help: "The Module to compare. If you specify something, target parameter will be ommited")
var module: String?

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

@Flag(help: "Use multi-edge or unique-edge configuration")
Expand Down
2 changes: 1 addition & 1 deletion Sources/jungle/Commands/DependantCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ struct DependantCommand: ParsableCommand {
abstract: "Outputs a sorted list of targets that depends on the specified one in target"
)

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

@Flag(help: "Show only Test targets")
Expand Down
2 changes: 1 addition & 1 deletion Sources/jungle/Commands/GraphCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ struct GraphCommand: ParsableCommand {
@Option(help: "The Module to compare. If you specify something, target parameter will be ommited")
var module: String?

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

@Flag(help: "Use multi-edge or unique-edge configuration")
Expand Down
10 changes: 5 additions & 5 deletions Sources/jungle/Commands/HistoryCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ struct HistoryCommand: AsyncParsableCommand {
@Option(help: "The Module to compare. If you specify something, target parameter will be ommited")
var module: String?

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

@Flag(help: "Use multi-edge or unique-edge configuration")
Expand Down Expand Up @@ -84,10 +84,10 @@ struct HistoryCommand: AsyncParsableCommand {
}

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

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

return try? await entry.process(
pod: module,
podfile: shell("git show \(entry.revision):Podfile.lock", at: directoryURL),
podfile: shell("git show \(entry.revision):./Podfile.lock", at: directoryURL),
target: entryTargetDependencies,
usingMultiEdge: useMultiedge
)
Expand Down
2 changes: 1 addition & 1 deletion Sources/jungle/Commands/Main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ struct Jungle: AsyncParsableCommand {
static var configuration = CommandConfiguration(
commandName: "jungle",
abstract: "SwiftPM and Cocoapods based projects complexity analyzer.",
version: "2.2.2",
version: "2.3.2",
subcommands: [HistoryCommand.self, CompareCommand.self, GraphCommand.self, ModulesCommand.self, DependantCommand.self],
defaultSubcommand: CompareCommand.self
)
Expand Down
2 changes: 1 addition & 1 deletion Sources/jungle/Commands/ModulesCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ struct ModulesCommand: ParsableCommand {
abstract: "Outputs a sorted list of modules dependencies count of your project"
)

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

@Argument(help: "Path to the directory where Podfile.lock or Package.swift is located")
Expand Down
90 changes: 90 additions & 0 deletions Tests/SPMExtractorTests/SPMExtractorTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -452,4 +452,94 @@ final class SPMExtractorTests: XCTestCase {
let dependant = try extractDependantTargets(from: rawPackage, target: "SamplePackage")
XCTAssertEqual(dependant.map(\.name).sorted(), ["Library", "LibraryTests", "SamplePackageTests"])
}

func testProductModulesFromPackage() throws {
let rawPackage = """
{
"dependencies" : [
{
"identity" : "swift-algorithms",
"requirement" : {
"range" : [
{
"lower_bound" : "1.0.0",
"upper_bound" : "2.0.0"
}
]
},
"type" : "sourceControl",
"url" : "https://github.com/apple/swift-algorithms"
}
],
"manifest_display_name" : "MyPackage",
"name" : "MyPackage",
"path" : "/Users/oswaldo.rubio/Desktop/MyPackage",
"platforms" : [

],
"products" : [
{
"name" : "MyPackage",
"targets" : [
"MyPackage"
],
"type" : {
"library" : [
"automatic"
]
}
},
{
"name" : "FeaturesContainer",
"targets" : [
"MyPackage"
],
"type" : {
"library" : [
"automatic"
]
}
}
],
"targets" : [
{
"c99name" : "MyPackageTests",
"module_type" : "SwiftTarget",
"name" : "MyPackageTests",
"path" : "Tests/MyPackageTests",
"sources" : [
"MyPackageTests.swift"
],
"target_dependencies" : [
"MyPackage"
],
"type" : "test"
},
{
"c99name" : "MyPackage",
"module_type" : "SwiftTarget",
"name" : "MyPackage",
"path" : "Sources/MyPackage",
"product_dependencies" : [
"Algorithms"
],
"product_memberships" : [
"MyPackage",
"FeaturesContainer"
],
"sources" : [
"MyPackage.swift"
],
"type" : "library"
}
],
"tools_version" : "5.8"
}
"""

let (dependencies, targetDependencies) = try extracPackageModules(from: rawPackage, target: "FeaturesContainer")

XCTAssertEqual(dependencies.map(\.name).sorted(), ["Algorithms", "MyPackage"])
XCTAssertEqual(targetDependencies.sorted(), ["MyPackage"])
}
}