Skip to content

Commit 38d1695

Browse files
authored
Allow specifying 2 .framework files to diff (#34)
* Allowing passing of .framework / .xcframework paths * Updating readme with new command * Styling README.md * Adding MIT license file * Applying template to README --------- Co-authored-by: Alex Guretzki <[email protected]>
1 parent 90c6b9b commit 38d1695

File tree

12 files changed

+287
-26
lines changed

12 files changed

+287
-26
lines changed

LICENSE.txt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2024 Adyen
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

Package.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ let package = Package(
4747
"PADSwiftInterfaceDiff",
4848
"PADOutputGenerator",
4949
"PADPackageFileAnalyzer",
50+
"PADSwiftInterfaceFileLocator",
5051
.product(name: "ArgumentParser", package: "swift-argument-parser")
5152
],
5253
path: "Sources/ExecutableTargets/CommandLineTool"
@@ -81,6 +82,7 @@ let package = Package(
8182
dependencies: [
8283
"PADCore",
8384
"PADLogging",
85+
"PADSwiftInterfaceFileLocator",
8486
"FileHandlingModule",
8587
"ShellModule",
8688
"SwiftPackageFileHelperModule"
@@ -104,6 +106,11 @@ let package = Package(
104106
dependencies: ["FileHandlingModule"],
105107
path: "Sources/Shared/Public/PADLogging"
106108
),
109+
.target(
110+
name: "PADSwiftInterfaceFileLocator",
111+
dependencies: ["FileHandlingModule", "ShellModule", "PADLogging"],
112+
path: "Sources/Shared/Public/PADSwiftInterfaceFileLocator"
113+
),
107114

108115
// MARK: - Shared/Package
109116

README.md

Lines changed: 78 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,29 @@
44

55
This tool allows comparing 2 versions of a swift (sdk) project and lists all changes in a human readable way.
66

7-
It makes use of `.swiftinterface` files that get produced during the archiving of a swift project and parses them using [`swift-syntax`](https://github.com/swiftlang/swift-syntax).
7+
It makes use of `.swiftinterface` files that get produced during the archiving of a swift project and parses them using [`swift-syntax`](https://github.com/swiftlang/swift-syntax).
8+
9+
## Contributing
10+
We strongly encourage you to contribute to our repository. Find out more in our [contribution guidelines](https://github.com/Adyen/.github/blob/master/CONTRIBUTING.md)
11+
12+
## Requirements
13+
- **Xcode** >= 16.0 (incl. Xcode command line tools)
14+
- **Swift** >= 5.9
815

916
## Usage
1017

1118
### From Project to Output
12-
19+
This method requires an iOS 17.5 Simulator to be installed
20+
21+
```
22+
swift run public-api-diff
23+
project
24+
--new "develop~https://github.com/Adyen/adyen-ios.git"
25+
--old "5.12.0~https://github.com/Adyen/adyen-ios.git"
26+
```
27+
28+
<details><summary><b>--help:</b></summary>
29+
1330
```
1431
USAGE: public-api-diff project --new <new> --old <old> [--scheme <scheme>] [--swift-interface-type <swift-interface-type>] [--output <output>] [--log-output <log-output>] [--log-level <log-level>]
1532
@@ -28,17 +45,18 @@ OPTIONS:
2845
(default: default)
2946
-h, --help Show help information.
3047
```
48+
</details>
49+
50+
### From `.swiftinterface` to Output
3151

32-
#### Run as debug build
3352
```
34-
# From Project to Output
3553
swift run public-api-diff
36-
project
37-
--new "develop~https://github.com/Adyen/adyen-ios.git"
38-
--old "5.12.0~https://github.com/Adyen/adyen-ios.git"
54+
swift-interface
55+
--new "new/path/to/project.swiftinterface"
56+
--old "old/path/to/project.swiftinterface"
3957
```
4058

41-
### From `.swiftinterface` to Output
59+
<details><summary><b>--help:</b></summary>
4260

4361
```
4462
USAGE: public-api-diff swift-interface --new <new> --old <old> [--target-name <target-name>] [--old-version-name <old-version-name>] [--new-version-name <new-version-name>] [--output <output>] [--log-output <log-output>] [--log-level <log-level>]
@@ -62,22 +80,53 @@ OPTIONS:
6280
(default: default)
6381
-h, --help Show help information.
6482
```
83+
</details>
84+
85+
### From `.framework` to Output
6586

66-
#### Run as debug build
6787
```
68-
# From Project to Output
6988
swift run public-api-diff
70-
swift-interface
71-
--new "new/path/to/project.swiftinterface"
72-
--old "old/path/to/project.swiftinterface"
89+
framework
90+
--target-name "TargetName"
91+
--new "new/path/to/project.framework"
92+
--old "old/path/to/project.framework"
7393
```
7494

75-
## How to create a release build
95+
<details><summary><b>--help:</b></summary>
96+
97+
```
98+
USAGE: public-api-diff framework --new <new> --old <old> --target-name <target-name> [--swift-interface-type <swift-interface-type>] [--old-version-name <old-version-name>] [--new-version-name <new-version-name>] [--output <output>] [--log-output <log-output>] [--log-level <log-level>]
99+
100+
OPTIONS:
101+
--new <new> Specify the updated .framework to compare to
102+
--old <old> Specify the old .framework to compare to
103+
--target-name <target-name>
104+
The name of your target/module to show in the output
105+
--swift-interface-type <swift-interface-type>
106+
[Optional] Specify the type of .swiftinterface you
107+
want to compare (public/private) (default: public)
108+
--old-version-name <old-version-name>
109+
[Optional] The name of your old version (e.g. v1.0 /
110+
main) to show in the output
111+
--new-version-name <new-version-name>
112+
[Optional] The name of your new version (e.g. v2.0 /
113+
develop) to show in the output
114+
--output <output> [Optional] Where to output the result (File path)
115+
--log-output <log-output>
116+
[Optional] Where to output the logs (File path)
117+
--log-level <log-level> [Optional] The log level to use during execution
118+
(default: default)
119+
-h, --help Show help information.
120+
```
121+
</details>
122+
123+
## Release Build
124+
### Create
76125
```
77126
swift build --configuration release
78127
```
79128

80-
## Run release build
129+
### Run
81130
```
82131
./public-api-diff
83132
project
@@ -88,13 +137,25 @@ swift build --configuration release
88137
swift-interface
89138
--new "new/path/to/project.swiftinterface"
90139
--old "old/path/to/project.swiftinterface"
140+
141+
./public-api-diff
142+
framework
143+
--target-name "TargetName"
144+
--new "new/path/to/project.framework"
145+
--old "old/path/to/project.framework"
91146
```
92147

93-
# Alternatives
148+
## Alternatives
94149
- **swift-api-digester**
95150
- `xcrun swift-api-digester -dump-sdk`
96151
- `xcrun swift-api-digester -diagnose-sdk`
97152

98-
# Inspiration
153+
## Inspiration
99154
- https://github.com/sdidla/Hatch/blob/main/Sources/Hatch/SymbolParser.swift
100155
- For parsing swift files using [swift-syntax](https://github.com/swiftlang/swift-syntax)'s [`SyntaxVisitor`](https://github.com/swiftlang/swift-syntax/blob/main/Sources/SwiftSyntax/generated/SyntaxVisitor.swift)
156+
157+
## Support
158+
If you have a feature request, or spotted a bug or a technical problem, create a GitHub issue.
159+
160+
## License
161+
MIT license. For more information, see the LICENSE file.

Sources/ExecutableTargets/CommandLineTool/CommandLineTool+Extensions.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import ArgumentParser
22

3-
import PADProjectBuilder
3+
import PADSwiftInterfaceFileLocator
44
import PADLogging
55

66
extension SwiftInterfaceType: ExpressibleByArgument {

Sources/ExecutableTargets/CommandLineTool/CommandLineTool.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ struct PublicApiDiff: AsyncParsableCommand {
2222
commandName: "public-api-diff",
2323
subcommands: [
2424
ProjectToOutputCommand.self,
25-
SwiftInterfaceToOutputCommand.self
25+
SwiftInterfaceToOutputCommand.self,
26+
FrameworkToOutputCommand.self
2627
]
2728
)
2829

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
import ArgumentParser
2+
import Foundation
3+
4+
import PADCore
5+
import PADLogging
6+
7+
import PADSwiftInterfaceDiff
8+
import PADSwiftInterfaceFileLocator
9+
import PADOutputGenerator
10+
import PADPackageFileAnalyzer
11+
12+
/// Command that analyzes the differences between an old and new project and produces a human readable output
13+
struct FrameworkToOutputCommand: AsyncParsableCommand {
14+
15+
static var configuration: CommandConfiguration = .init(commandName: "framework")
16+
17+
/// The path to the new/updated xcframework
18+
@Option(help: "Specify the updated .framework to compare to")
19+
public var new: String
20+
21+
/// The path to the old/reference xcframework
22+
@Option(help: "Specify the old .framework to compare to")
23+
public var old: String
24+
25+
/// The name of the target/module to show in the output
26+
@Option(help: "The name of your target/module to show in the output")
27+
public var targetName: String
28+
29+
@Option(help: "[Optional] Specify the type of .swiftinterface you want to compare (public/private)")
30+
public var swiftInterfaceType: SwiftInterfaceType = .public
31+
32+
@Option(help: "[Optional] The name of your old version (e.g. v1.0 / main) to show in the output")
33+
public var oldVersionName: String?
34+
35+
@Option(help: "[Optional] The name of your new version (e.g. v2.0 / develop) to show in the output")
36+
public var newVersionName: String?
37+
38+
/// The (optional) output file path
39+
///
40+
/// If not defined the output will be printed to the console
41+
@Option(help: "[Optional] Where to output the result (File path)")
42+
public var output: String?
43+
44+
/// The (optional) path to the log output file
45+
@Option(help: "[Optional] Where to output the logs (File path)")
46+
public var logOutput: String?
47+
48+
@Option(help: "[Optional] The log level to use during execution")
49+
public var logLevel: LogLevel = .default
50+
51+
/// Entry point of the command line tool
52+
public func run() async throws {
53+
54+
let logger = PublicApiDiff.logger(with: logLevel, logOutputFilePath: logOutput)
55+
56+
do {
57+
// MARK: - Locating .swiftinterface files
58+
59+
let swiftInterfaceFiles = try Self.locateSwiftInterfaceFiles(
60+
targetName: targetName,
61+
oldPath: old,
62+
newPath: new,
63+
swiftInterfaceType: swiftInterfaceType,
64+
logger: logger
65+
)
66+
67+
// MARK: - Analyzing .swiftinterface files
68+
69+
let swiftInterfaceChanges = try await Self.analyzeSwiftInterfaceFiles(
70+
swiftInterfaceFiles: swiftInterfaceFiles,
71+
logger: logger
72+
)
73+
74+
// MARK: - Generate Output
75+
76+
let generatedOutput = try Self.generateOutput(
77+
for: swiftInterfaceChanges,
78+
warnings: [],
79+
allTargets: [targetName],
80+
oldVersionName: oldVersionName,
81+
newVersionName: newVersionName
82+
)
83+
84+
// MARK: -
85+
86+
if let output {
87+
try FileManager.default.write(generatedOutput, to: output)
88+
} else {
89+
// We're not using a logger here as we always want to have it printed if no output was specified
90+
print(generatedOutput)
91+
}
92+
93+
logger.log("✅ Success", from: "Main")
94+
} catch {
95+
logger.log("💥 \(error.localizedDescription)", from: "Main")
96+
}
97+
}
98+
}
99+
100+
// MARK: - Privates
101+
102+
private extension FrameworkToOutputCommand {
103+
104+
static func locateSwiftInterfaceFiles(
105+
targetName: String,
106+
oldPath: String,
107+
newPath: String,
108+
swiftInterfaceType: SwiftInterfaceType,
109+
logger: any Logging
110+
) throws -> [SwiftInterfaceFile] {
111+
let locator = SwiftInterfaceFileLocator(logger: logger)
112+
113+
let oldSwiftInterfaceFileUrl = try locator.locate(
114+
for: targetName,
115+
derivedDataPath: oldPath,
116+
type: swiftInterfaceType
117+
)
118+
let newSwiftInterfaceFileUrl = try locator.locate(
119+
for: targetName,
120+
derivedDataPath: newPath,
121+
type: swiftInterfaceType
122+
)
123+
124+
return [.init(
125+
name: targetName,
126+
oldFilePath: oldSwiftInterfaceFileUrl.path(),
127+
newFilePath: newSwiftInterfaceFileUrl.path()
128+
)]
129+
}
130+
131+
static func analyzeSwiftInterfaceFiles(
132+
swiftInterfaceFiles: [SwiftInterfaceFile],
133+
logger: any Logging
134+
) async throws -> [String: [Change]] {
135+
let swiftInterfaceDiff = SwiftInterfaceDiff(logger: logger)
136+
137+
return try await swiftInterfaceDiff.run(
138+
with: swiftInterfaceFiles
139+
)
140+
}
141+
142+
static func generateOutput(
143+
for changes: [String: [Change]],
144+
warnings: [String],
145+
allTargets: [String]?,
146+
oldVersionName: String?,
147+
newVersionName: String?
148+
) throws -> String {
149+
let outputGenerator: any OutputGenerating<String> = MarkdownOutputGenerator()
150+
151+
return try outputGenerator.generate(
152+
from: changes,
153+
allTargets: allTargets,
154+
oldVersionName: oldVersionName,
155+
newVersionName: newVersionName,
156+
warnings: warnings
157+
)
158+
}
159+
}

Sources/ExecutableTargets/CommandLineTool/ProjectToOutputCommand.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import Foundation
33

44
import PADCore
55
import PADLogging
6+
import PADSwiftInterfaceFileLocator
67

78
import PADSwiftInterfaceDiff
89
import PADProjectBuilder

Sources/ExecutableTargets/CommandLineTool/SwiftInterfaceToOutputCommand.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@ struct SwiftInterfaceToOutputCommand: AsyncParsableCommand {
1414

1515
static var configuration: CommandConfiguration = .init(commandName: "swift-interface")
1616

17-
/// The representation of the new/updated project source
17+
/// The path to the new/updated .swiftinterface file
1818
@Option(help: "Specify the updated .swiftinterface file to compare to")
1919
public var new: String
2020

21-
/// The representation of the old/reference project source
21+
/// The path to the old/reference .swiftinterface file
2222
@Option(help: "Specify the old .swiftinterface file to compare to")
2323
public var old: String
2424

0 commit comments

Comments
 (0)