Skip to content
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
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ Alternatively you could use `xcrun swift-api-digester -diagnose-sdk` and pass th

## How it works

![image](https://github.com/user-attachments/assets/6f8b8927-d08b-487d-9d80-e5ee1b8d8302)
![image](https://github.com/user-attachments/assets/cc04d21a-06f6-42bc-8e73-4aef7af21d7a)


### Project Builder

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ private extension SDKDump.Element {
if isObjcAccessible { components += ["@objc"] }
if isInlinable { components += ["@inlinable"] }
if isOverride { components += ["override"] }
if declKind != .import {
if declKind != .import && declKind != .case {
if isOpen {
components += ["open"]
} else if isInternal {
Expand Down
191 changes: 178 additions & 13 deletions Sources/Helpers/Models/SwiftPackageDescription.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,43 +6,150 @@

import Foundation

struct SwiftPackageDescription: Codable {
// See: https://docs.swift.org/package-manager/PackageDescription/index.html
// See: https://github.com/swiftlang/swift-package-manager/blob/main/Sources/PackageDescription/PackageDescriptionSerialization.swift

struct SwiftPackageDescription: Codable, Equatable {

let name: String
let platforms: [Platform]
let defaultLocalization: String?
let products: [Product]

let targets: [Target]
let products: [Product]
let dependencies: [Dependency]

let toolsVersion: String

var warnings = [String]()

init(
defaultLocalization: String?,
name: String,
platforms: [Platform] = [],
products: [Product] = [],
targets: [Target] = [],
dependencies: [Dependency] = [],
toolsVersion: String,
warnings: [String] = []
) {
self.defaultLocalization = defaultLocalization
self.name = name
self.platforms = platforms
self.products = products
self.targets = targets
self.dependencies = dependencies
self.toolsVersion = toolsVersion
self.warnings = warnings
}

enum CodingKeys: String, CodingKey {
case defaultLocalization = "default_localization"
case name
case platforms
case products
case targets
case dependencies
case toolsVersion = "tools_version"
}
}

extension SwiftPackageDescription {

struct Product: Codable {
struct Platform: Codable, Equatable, Hashable {

let name: String
let version: String
}
}

extension SwiftPackageDescription.Platform: CustomStringConvertible {
var description: String {
"\(name)(\(version))"
}
}

extension SwiftPackageDescription {

struct Product: Codable, Equatable, Hashable {

// TODO: Add `rule` property

let name: String
let targets: [String]
}
}

extension SwiftPackageDescription.Product: CustomStringConvertible {
var description: String {
let targetsDescription = targets.map { "\"\($0)\"" }.joined(separator: ", ")
return ".library(name: \"\(name)\", targets: [\(targetsDescription)])"
}
}

extension SwiftPackageDescription {

struct Dependency: Codable, Equatable {

let identity: String
let requirement: Requirement
let type: String
let url: String?
}
}

extension SwiftPackageDescription.Dependency: CustomStringConvertible {

var description: String {
var description = ".package("

var fields = [String]()

if let url {
fields += ["url: \"\(url)\""]
}

fields += [requirement.description]

description += fields.joined(separator: ", ")

description += ")"
return description
}
}

extension SwiftPackageDescription.Dependency {

struct Requirement: Codable, Equatable {

// TODO: Which other requirements exist?

let exact: [String]?
}
}

extension SwiftPackageDescription.Dependency.Requirement: CustomStringConvertible {

var description: String {
if let exactVersion = exact?.first {
return "exact: \"\(exactVersion)\""
}

return "UNKNOWN_REQUIREMENT"
}
}

extension SwiftPackageDescription {

struct Target: Codable {
struct Target: Codable, Equatable {

enum ModuleType: String, Codable {
enum ModuleType: String, Codable, Equatable {
case swiftTarget = "SwiftTarget"
case binaryTarget = "BinaryTarget"
case clangTarget = "ClangTarget"
}

enum TargetType: String, Codable {
enum TargetType: String, Codable, Equatable {
case library = "library"
case binary = "binary"
case test = "test"
Expand All @@ -53,30 +160,88 @@ extension SwiftPackageDescription {
let path: String
let moduleType: ModuleType

/// `.product(name: ...)` dependency
let productDependencies: [String]?
let productMemberships: [String]?
let sources: [String]
/// `.target(name: ...) dependency
let targetDependencies: [String]?

let resources: [Resource]?
// Ignoring following properties for now as they are not handled in the `PackageAnalyzer`
// and thus would produce changes that are not visible
//
// let productMemberships: [String]?
// let sources: [String]
// let resources: [Resource]?

init(
name: String,
type: TargetType,
path: String,
moduleType: ModuleType,
productDependencies: [String]? = nil,
targetDependencies: [String]? = nil
) {
self.name = name
self.type = type
self.path = path
self.moduleType = moduleType
self.productDependencies = productDependencies
self.targetDependencies = targetDependencies
}

enum CodingKeys: String, CodingKey {
case moduleType = "module_type"
case name
case productDependencies = "product_dependencies"
case productMemberships = "product_memberships"
case sources
case targetDependencies = "target_dependencies"
case type
case path
case resources
}
}
}

extension SwiftPackageDescription.Target.TargetType: CustomStringConvertible {
var description: String {
switch self {
case .binary: "binaryTarget"
case .library: "target"
case .test: "testTarget"
}
}
}

extension SwiftPackageDescription.Target: CustomStringConvertible {

var description: String {
var description = ".\(type.description)(name: \"\(name)\""

var dependencyDescriptions = [String]()

if let targetDependenciesDescriptions = targetDependencies?.map({ ".target(name: \"\($0)\")" }) {
dependencyDescriptions += targetDependenciesDescriptions
}

if let productDependenciesDescriptions = productDependencies?.map({ ".product(name: \"\($0)\", ...)" }) {
dependencyDescriptions += productDependenciesDescriptions
}

if !dependencyDescriptions.isEmpty {
// `, dependencies: [.target(name: ...), .target(name: ...), .product(name: ...), ...]`
description += ", dependencies: [\(dependencyDescriptions.joined(separator: ", "))]"
}

description += ", path: \"\(path)\""

description += ")"

return description
}
}

extension SwiftPackageDescription.Target {

struct Resource: Codable {
struct Resource: Codable, Equatable {

// TODO: Add `rule` property

let path: String
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import Foundation

enum PackageFileHelperError: LocalizedError {
enum SwiftPackageFileHelperError: LocalizedError {
case packageDescriptionError(_ description: String)
case couldNotGeneratePackageDescription
case couldNotConsolidateTargetsInPackageFile
Expand All @@ -23,7 +23,7 @@ enum PackageFileHelperError: LocalizedError {
}
}

struct PackageFileHelper {
struct SwiftPackageFileHelper {

private let fileHandler: FileHandling
private let xcodeTools: XcodeTools
Expand Down Expand Up @@ -89,7 +89,7 @@ struct PackageFileHelper {

// MARK: Generate Package Description

private extension PackageFileHelper {
private extension SwiftPackageFileHelper {

func generatePackageDescription(at projectDirectoryPath: String) throws -> SwiftPackageDescription {

Expand All @@ -109,7 +109,7 @@ private extension PackageFileHelper {
// That we have to get rid of first to generate the description object

if firstLine.starts(with: errorTag) {
throw PackageFileHelperError.packageDescriptionError(result)
throw SwiftPackageFileHelperError.packageDescriptionError(result)
}

if firstLine.starts(with: warningTag) {
Expand All @@ -120,23 +120,29 @@ private extension PackageFileHelper {
warnings += [warning]
}

if firstLine.starts(with: "{"),
let packageDescriptionData = packageDescriptionLines.joined(separator: newLine).data(using: .utf8) {
var packageDescription = try JSONDecoder().decode(SwiftPackageDescription.self, from: packageDescriptionData)
packageDescription.warnings = warnings
return packageDescription
if
firstLine.starts(with: "{"),
let packageDescriptionData = packageDescriptionLines.joined(separator: newLine).data(using: .utf8)
{
return try decodePackageDescription(from: packageDescriptionData, warnings: warnings)
}

packageDescriptionLines.removeFirst()
}

throw PackageFileHelperError.couldNotGeneratePackageDescription
throw SwiftPackageFileHelperError.couldNotGeneratePackageDescription
}

func decodePackageDescription(from packageDescriptionData: Data, warnings: [String]) throws -> SwiftPackageDescription {
var packageDescription = try JSONDecoder().decode(SwiftPackageDescription.self, from: packageDescriptionData)
packageDescription.warnings = warnings
return packageDescription
}
}

// MARK: Update Package Content

private extension PackageFileHelper {
private extension SwiftPackageFileHelper {

/// Generates a library entry from the name and available target names to be inserted into the `Package.swift` file
func consolidatedLibraryEntry(
Expand All @@ -162,7 +168,7 @@ private extension PackageFileHelper {
if let productsRange = packageContent.range(of: "products: [", options: .caseInsensitive) {
updatedContent.insert(contentsOf: consolidatedEntry, at: productsRange.upperBound)
} else {
throw PackageFileHelperError.couldNotConsolidateTargetsInPackageFile
throw SwiftPackageFileHelperError.couldNotConsolidateTargetsInPackageFile
}
return updatedContent
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/Pipeline/Modules/ABIGenerator/ABIGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ struct ABIGenerator: ABIGenerating {

private let shell: ShellHandling
private let xcodeTools: XcodeTools
private let packageFileHelper: PackageFileHelper
private let packageFileHelper: SwiftPackageFileHelper
private let fileHandler: FileHandling
private let logger: Logging?

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ struct PackageABIGenerator: ABIGenerating {

let fileHandler: FileHandling
let xcodeTools: XcodeTools
let packageFileHelper: PackageFileHelper
let packageFileHelper: SwiftPackageFileHelper
let logger: Logging?

func generate(
Expand Down
4 changes: 2 additions & 2 deletions Sources/Pipeline/Modules/ProjectBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -179,10 +179,10 @@ private extension ProjectBuilder {
// If a project scheme was provided we just try to build it
schemeToBuild = scheme
} else {
// Creating an `.library(name: "_allTargets", targets: [ALL_TARGETS])`
// Creating an `.library(name: "_AllTargets", targets: [ALL_TARGETS])`
// so we only have to build once and then can generate ABI files for every module from a single build
schemeToBuild = "_AllTargets"
let packageFileHelper = PackageFileHelper(fileHandler: fileHandler, xcodeTools: xcodeTools)
let packageFileHelper = SwiftPackageFileHelper(fileHandler: fileHandler, xcodeTools: xcodeTools)
try packageFileHelper.preparePackageWithConsolidatedLibrary(named: schemeToBuild, at: projectDirectoryPath)
}

Expand Down
Loading
Loading