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

Parse dump-pif result and reduce XCBuildSupport dependency #183

Open
wants to merge 19 commits into
base: main
Choose a base branch
from
Open
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
27 changes: 17 additions & 10 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,6 @@
import PackageDescription
import Foundation

let swiftSettings: [SwiftSetting] = [
.enableExperimentalFeature("StrictConcurrency"),
.unsafeFlags(["-strict-concurrency=complete"]),
]

let package = Package(
name: "Scipio",
platforms: [
Expand Down Expand Up @@ -36,19 +31,21 @@ let package = Package(
from: "4.0.1"),
.package(url: "https://github.com/giginet/scipio-cache-storage.git",
from: "1.0.0"),
.package(url: "https://github.com/SwiftyJSON/SwiftyJSON.git",
from: "5.0.0"),
],
targets: [
.executableTarget(
name: "scipio",
dependencies: [
.target(name: "ScipioKit"),
.product(name: "ArgumentParser", package: "swift-argument-parser"),
],
swiftSettings: swiftSettings
]
),
.target(
name: "ScipioKit",
dependencies: [
.target(name: "PIFKit"),
.product(name: "SwiftPMDataModel-auto", package: "swift-package-manager"),
.product(name: "XCBuildSupport", package: "swift-package-manager"),
.product(name: "Logging", package: "swift-log"),
Expand All @@ -57,11 +54,16 @@ let package = Package(
.product(name: "Rainbow", package: "Rainbow"),
.product(name: "ScipioStorage", package: "scipio-cache-storage"),
],
swiftSettings: swiftSettings,
plugins: [
.plugin(name: "GenerateScipioVersion")
]
),
.target(
name: "PIFKit",
dependencies: [
.product(name: "SwiftyJSON", package: "SwiftyJSON"),
]
),
.plugin(
name: "GenerateScipioVersion",
capability: .buildTool()
Expand All @@ -72,9 +74,14 @@ let package = Package(
.target(name: "ScipioKit"),
],
exclude: ["Resources/Fixtures/"],
resources: [.copy("Resources/Fixtures")],
swiftSettings: swiftSettings
resources: [.copy("Resources/Fixtures")]
),
.testTarget(
name: "PIFKitTests",
dependencies: [
.target(name: "PIFKit"),
]
)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
)
),

],
swiftLanguageModes: [.v6]
)
Expand Down
101 changes: 101 additions & 0 deletions Sources/PIFKit/BuildConfiguration.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import Foundation
import SwiftyJSON

/// A build configuration for a target.
package struct BuildConfiguration: Codable, Equatable, JSONConvertible {
Comment on lines +4 to +5
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please add the reference to its doc comments?

package enum MacroExpressionValue: Sendable, Codable, Equatable {
case bool(Bool)
case string(String)
case stringList([String])
}

package struct ImpartedBuildProperties: Sendable, Codable, Equatable {
package var buildSettings: [String: MacroExpressionValue]

package init(buildSettings: [String: MacroExpressionValue]) {
self.buildSettings = buildSettings
}
}

package var name: String
package var buildSettings: [String: MacroExpressionValue]
package var baseConfigurationFileReferenceGUID: String?
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this?

package var impartedBuildProperties: ImpartedBuildProperties
}

extension BuildConfiguration.MacroExpressionValue {
package enum DecodingError: Error {
case unknownBuildSettingsValue
}

package func encode(to encoder: any Encoder) throws {
var container = encoder.singleValueContainer()
switch self {
case .bool(let value):
try container.encode(value ? "YES" : "NO")
case .string(let value):
try container.encode(value)
case .stringList(let value):
try container.encode(value)
}
}

package init(from decoder: any Decoder) throws {
let container = try decoder.singleValueContainer()

if let stringValue = try? container.decode(String.self) {
if stringValue == "YES" {
self = .bool(true)
} else if stringValue == "NO" {
self = .bool(false)
} else {
self = .string(stringValue)
}
} else if let arrayValue = try? container.decode([String].self) {
self = .stringList(arrayValue)
} else {
throw DecodingError.unknownBuildSettingsValue
}
}
}

extension BuildConfiguration.MacroExpressionValue: ExpressibleByStringLiteral {
package init(stringLiteral value: String) {
self = .string(value)
}
}

extension BuildConfiguration.MacroExpressionValue: ExpressibleByBooleanLiteral {
package init(booleanLiteral value: Bool) {
self = .bool(value)
}
}

extension BuildConfiguration.MacroExpressionValue: ExpressibleByArrayLiteral {
package init(arrayLiteral elements: String...) {
self = .stringList(elements)
}
}

extension BuildConfiguration.MacroExpressionValue? {
package mutating func append(_ appendingValues: [String]) {
self = .stringList(self.values + appendingValues)
}

package mutating func append(_ appendingValue: String) {
append([appendingValue])
}

private var values: [String] {
switch self {
case .bool(let value):
return [value ? "YES" : "NO"]
case .string(let value):
return [value]
case .stringList(let value):
return value
case .none:
return ["$(inherited)"]
}
}
}
12 changes: 12 additions & 0 deletions Sources/PIFKit/JSONConvertible.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import Foundation
import SwiftyJSON

package protocol JSONConvertible: Codable { }

extension JSONConvertible {
init(from json: JSON) throws {
let decoder = JSONDecoder()
let data = try json.rawData()
self = try decoder.decode(Self.self, from: data)
}
}
50 changes: 50 additions & 0 deletions Sources/PIFKit/PIFManipulator.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import Foundation
import SwiftyJSON

/// Manipulates PIF JSON data
package class PIFManipulator {
private var topLevelObject: JSON

/// Initialize PIFManipulator with JSON data
/// - Parameters jsonData: JSON data
package init(jsonData: Data) throws {
self.topLevelObject = try JSON(data: jsonData)
}

/// Update targets in PIF JSON data
/// - Parameters modifier: Closure to modify Target
package func updateTargets(_ modifier: (inout Target) -> Void) {
for index in 0..<topLevelObject.arrayValue.count {
guard topLevelObject[index]["type"].stringValue == "target", var target = try? Target(from: topLevelObject[index]["contents"]) else {
continue
}

modifier(&target)
apply(target, to: &topLevelObject[index])
}
}

/// Dump manipulating JSON data
/// - Returns: JSON data
package func dump() throws -> Data {
try topLevelObject.rawData(options: [.prettyPrinted])
}

/// Apply target to JSON object
/// Currently, Target is a subset of an actual PIF target. So, we have to apply only the properties that are present in the Target.
/// - Parameters target: Target to apply
/// - Parameters pifObject: JSON object to apply the target to
private func apply(_ target: Target, to pifObject: inout JSON) {
pifObject["contents"]["name"].string = target.name
pifObject["contents"]["productTypeIdentifier"].string = target.productType?.rawValue
pifObject["contents"]["buildConfigurations"].arrayObject = target.buildConfigurations.compactMap { try? $0.toJSON() }
}

Comment on lines +40 to +42
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be better to reuse a single JSONEncoder I think.

Suggested change
pifObject["contents"]["buildConfigurations"].arrayObject = target.buildConfigurations.compactMap { try? $0.toJSON() }
}
let jsonEncoder = JSONEncoder()
pifObject["contents"]["buildConfigurations"].arrayObject = target.buildConfigurations.compactMap { try? $0.toJSON(using: jsonEncoder) }
}

}

extension BuildConfiguration {
fileprivate func toJSON(using encoder: JSONEncoder = JSONEncoder()) throws -> JSON {
let data = try encoder.encode(self)
return try JSON(data: data)
}
}
26 changes: 26 additions & 0 deletions Sources/PIFKit/Target.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import Foundation
import SwiftyJSON

package struct Target: Sendable, Codable, Equatable, JSONConvertible {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please add the reference to its doc comments?

package enum ProductType: String, Codable, Equatable, Sendable {
case application = "com.apple.product-type.application"
case staticArchive = "com.apple.product-type.library.static"
case objectFile = "com.apple.product-type.objfile"
case dynamicLibrary = "com.apple.product-type.library.dynamic"
case framework = "com.apple.product-type.framework"
case executable = "com.apple.product-type.tool"
case unitTest = "com.apple.product-type.bundle.unit-test"
case bundle = "com.apple.product-type.bundle"
case packageProduct = "packageProduct"
}

package var name: String
package var buildConfigurations: [BuildConfiguration]
package var productType: ProductType?

enum CodingKeys: String, CodingKey {
case name
case buildConfigurations
case productType = "productTypeIdentifier"
}
}
5 changes: 3 additions & 2 deletions Sources/ScipioKit/Producer/PIF/PIFCompiler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,13 @@ struct PIFCompiler: Compiler {
let buildParameters = try makeBuildParameters(toolchain: toolchain)

let generator = try PIFGenerator(
package: descriptionPackage,
packageName: descriptionPackage.name,
packageLocator: descriptionPackage,
buildParameters: buildParameters,
buildOptions: buildOptions,
buildOptionsMatrix: buildOptionsMatrix
)
let pifPath = try generator.generateJSON(for: sdk)
let pifPath = try await generator.generateJSON(for: sdk)
let buildParametersPath = try buildParametersGenerator.generate(
for: sdk,
buildParameters: buildParameters,
Expand Down
Loading