Skip to content

Commit 8cdced9

Browse files
hartbitaciidgh
authored andcommitted
Migrate PackageDescription4 to Codable
<rdar://problem/40207132> Adopt Codable in the runtime libraries
1 parent 088937e commit 8cdced9

12 files changed

+183
-335
lines changed

Sources/PackageDescription4/JSON.swift

Lines changed: 0 additions & 50 deletions
This file was deleted.

Sources/PackageDescription4/LanguageStandardSettings.swift

Lines changed: 11 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
/*
22
This source file is part of the Swift.org open source project
33

4-
Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
4+
Copyright (c) 2018 Apple Inc. and the Swift project authors
55
Licensed under Apache License v2.0 with Runtime Library Exception
66

77
See http://swift.org/LICENSE.txt for license information
88
See http://swift.org/CONTRIBUTORS.txt for Swift project authors
99
*/
1010

1111
/// Support C language standards.
12-
public enum CLanguageStandard: String {
12+
public enum CLanguageStandard: String, Encodable {
1313
case c89
1414
case c90
1515
case iso9899_1990 = "iso9899:1990"
@@ -25,7 +25,7 @@ public enum CLanguageStandard: String {
2525
}
2626

2727
/// Supported C++ language standards.
28-
public enum CXXLanguageStandard: String {
28+
public enum CXXLanguageStandard: String, Encodable {
2929
case cxx98 = "c++98"
3030
case cxx03 = "c++03"
3131
case gnucxx98 = "gnu++98"
@@ -52,32 +52,19 @@ public enum SwiftVersion {
5252
case version(String)
5353
}
5454

55-
extension SwiftVersion {
56-
func toString() -> String {
57-
let value: String
55+
extension SwiftVersion: Encodable {
56+
public func encode(to encoder: Encoder) throws {
57+
var container = encoder.singleValueContainer()
5858
switch self {
5959
case .v3:
60-
value = "3"
60+
try container.encode("3")
6161
case .v4:
62-
value = "4"
62+
try container.encode("4")
6363
case .v4_2:
64-
value = "4.2"
65-
case .version(let v):
66-
value = v
64+
try container.encode("4.2")
65+
case .version(let value):
66+
try container.encode(value)
6767
}
68-
return value
6968
}
7069
}
7170
#endif
72-
73-
extension CLanguageStandard {
74-
func toJSON() -> JSON {
75-
return .string(self.rawValue)
76-
}
77-
}
78-
79-
extension CXXLanguageStandard {
80-
func toJSON() -> JSON {
81-
return .string(self.rawValue)
82-
}
83-
}

Sources/PackageDescription4/Package.swift

Lines changed: 87 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
This source file is part of the Swift.org open source project
33

4-
Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
4+
Copyright (c) 2018 Apple Inc. and the Swift project authors
55
Licensed under Apache License v2.0 with Runtime Library Exception
66

77
See http://swift.org/LICENSE.txt for license information
@@ -13,12 +13,13 @@ import Glibc
1313
#else
1414
import Darwin.C
1515
#endif
16+
import Foundation
1617

1718
/// The description for a complete package.
1819
public final class Package {
1920

2021
/// Represents a package dependency.
21-
public class Dependency {
22+
public class Dependency: Encodable {
2223

2324
/// The dependency requirement.
2425
public enum Requirement {
@@ -192,168 +193,136 @@ public enum SystemPackageProvider {
192193

193194
// MARK: Package JSON serialization
194195

195-
extension SystemPackageProvider {
196+
extension Package: Encodable {
197+
private enum CodingKeys: CodingKey {
198+
case name
199+
case pkgConfig
200+
case providers
201+
case products
202+
case dependencies
203+
case targets
204+
case swiftLanguageVersions
205+
case cLanguageStandard
206+
case cxxLanguageStandard
207+
}
208+
209+
public func encode(to encoder: Encoder) throws {
210+
var container = encoder.container(keyedBy: CodingKeys.self)
211+
try container.encode(name, forKey: .name)
212+
try container.encode(pkgConfig, forKey: .pkgConfig)
213+
try container.encode(providers, forKey: .providers)
214+
try container.encode(products, forKey: .products)
215+
try container.encode(dependencies, forKey: .dependencies)
216+
try container.encode(targets, forKey: .targets)
217+
#if PACKAGE_DESCRIPTION_4
218+
try container.encode(swiftLanguageVersions?.map(String.init), forKey: .swiftLanguageVersions)
219+
#else
220+
try container.encode(swiftLanguageVersions, forKey: .swiftLanguageVersions)
221+
#endif
222+
try container.encode(cLanguageStandard, forKey: .cLanguageStandard)
223+
try container.encode(cxxLanguageStandard, forKey: .cxxLanguageStandard)
224+
}
225+
}
226+
227+
extension SystemPackageProvider: Encodable {
228+
private enum CodingKeys: CodingKey {
229+
case name
230+
case values
231+
}
196232

197-
func toJSON() -> JSON {
198-
let name: String
199-
let values: [String]
233+
private enum Name: String, Encodable {
234+
case brew
235+
case apt
236+
}
200237

238+
public func encode(to encoder: Encoder) throws {
239+
var container = encoder.container(keyedBy: CodingKeys.self)
201240
#if PACKAGE_DESCRIPTION_4
202241
switch self {
203242
case .brewItem(let packages):
204-
name = "brew"
205-
values = packages
243+
try container.encode(Name.brew, forKey: .name)
244+
try container.encode(packages, forKey: .values)
206245
case .aptItem(let packages):
207-
name = "apt"
208-
values = packages
246+
try container.encode(Name.apt, forKey: .name)
247+
try container.encode(packages, forKey: .values)
209248
}
210249
#else
211250
switch self {
212251
case ._brewItem(let packages):
213-
name = "brew"
214-
values = packages
252+
try container.encode(Name.brew, forKey: .name)
253+
try container.encode(packages, forKey: .values)
215254
case ._aptItem(let packages):
216-
name = "apt"
217-
values = packages
255+
try container.encode(Name.apt, forKey: .name)
256+
try container.encode(packages, forKey: .values)
218257
}
219258
#endif
220-
221-
return .dictionary([
222-
"name": .string(name),
223-
"values": .array(values.map(JSON.string)),
224-
])
225259
}
226260
}
227261

228-
extension Package {
229-
func toJSON() -> JSON {
230-
var dict: [String: JSON] = [:]
231-
dict["name"] = .string(name)
232-
if let pkgConfig = self.pkgConfig {
233-
dict["pkgConfig"] = .string(pkgConfig)
234-
}
235-
dict["dependencies"] = .array(dependencies.map({ $0.toJSON() }))
236-
dict["targets"] = .array(targets.map({ $0.toJSON() }))
237-
dict["products"] = .array(products.map({ $0.toJSON() }))
238-
if let providers = self.providers {
239-
dict["providers"] = .array(providers.map({ $0.toJSON() }))
240-
}
241-
242-
let swiftLanguageVersionsString: [String]?
243-
#if PACKAGE_DESCRIPTION_4
244-
swiftLanguageVersionsString = self.swiftLanguageVersions?.map(String.init)
245-
#else
246-
swiftLanguageVersionsString = self.swiftLanguageVersions?.map({ $0.toString() })
247-
#endif
248-
if let swiftLanguageVersions = swiftLanguageVersionsString {
249-
dict["swiftLanguageVersions"] = .array(swiftLanguageVersions.map(JSON.string))
250-
}
251-
252-
dict["cLanguageStandard"] = cLanguageStandard?.toJSON() ?? .null
253-
dict["cxxLanguageStandard"] = cxxLanguageStandard?.toJSON() ?? .null
254-
return .dictionary(dict)
262+
extension Target.Dependency: Encodable {
263+
private enum CodingKeys: CodingKey {
264+
case type
265+
case name
266+
case package
255267
}
256-
}
257268

258-
extension Target {
259-
func toJSON() -> JSON {
260-
var dict: [String: JSON] = [
261-
"name": .string(name),
262-
"type": .string(type.rawValue),
263-
"publicHeadersPath": publicHeadersPath.map(JSON.string) ?? JSON.null,
264-
"dependencies": .array(dependencies.map({ $0.toJSON() })),
265-
"path": path.map(JSON.string) ?? JSON.null,
266-
"exclude": .array(exclude.map(JSON.string)),
267-
"sources": sources.map({ JSON.array($0.map(JSON.string)) }) ?? JSON.null,
268-
]
269-
if let pkgConfig = self.pkgConfig {
270-
dict["pkgConfig"] = .string(pkgConfig)
271-
}
272-
if let providers = self.providers {
273-
dict["providers"] = .array(providers.map({ $0.toJSON() }))
274-
}
275-
return .dictionary(dict)
269+
private enum Kind: String, Codable {
270+
case target
271+
case product
272+
case byName = "byname"
276273
}
277-
}
278-
279-
extension Target.Dependency {
280-
func toJSON() -> JSON {
281-
var dict = [String: JSON]()
282274

275+
public func encode(to encoder: Encoder) throws {
276+
var container = encoder.container(keyedBy: CodingKeys.self)
283277
#if PACKAGE_DESCRIPTION_4
284278
switch self {
285279
case .targetItem(let name):
286-
dict["name"] = .string(name)
287-
dict["type"] = .string("target")
280+
try container.encode(Kind.target, forKey: .type)
281+
try container.encode(name, forKey: .name)
288282
case .productItem(let name, let package):
289-
dict["name"] = .string(name)
290-
dict["type"] = .string("product")
291-
dict["package"] = package.map(JSON.string) ?? .null
283+
try container.encode(Kind.product, forKey: .type)
284+
try container.encode(name, forKey: .name)
285+
try container.encode(package, forKey: .package)
292286
case .byNameItem(let name):
293-
dict["name"] = .string(name)
294-
dict["type"] = .string("byname")
287+
try container.encode(Kind.byName, forKey: .type)
288+
try container.encode(name, forKey: .name)
295289
}
296290
#else
297291
switch self {
298292
case ._targetItem(let name):
299-
dict["name"] = .string(name)
300-
dict["type"] = .string("target")
293+
try container.encode(Kind.target, forKey: .type)
294+
try container.encode(name, forKey: .name)
301295
case ._productItem(let name, let package):
302-
dict["name"] = .string(name)
303-
dict["type"] = .string("product")
304-
dict["package"] = package.map(JSON.string) ?? .null
296+
try container.encode(Kind.product, forKey: .type)
297+
try container.encode(name, forKey: .name)
298+
try container.encode(package, forKey: .package)
305299
case ._byNameItem(let name):
306-
dict["name"] = .string(name)
307-
dict["type"] = .string("byname")
300+
try container.encode(Kind.byName, forKey: .type)
301+
try container.encode(name, forKey: .name)
308302
}
309303
#endif
310-
311-
return .dictionary(dict)
312304
}
313305
}
314306

315307
// MARK: Package Dumping
316308

317-
struct Errors {
318-
/// Storage to hold the errors.
319-
private var errors = [String]()
320-
321-
/// Adds error to global error array which will be serialized and dumped in
322-
/// JSON at exit.
323-
mutating func add(_ str: String) {
324-
// FIXME: This will produce invalid JSON if string contains quotes.
325-
// Assert it for now and fix when we have escaping in JSON.
326-
assert(!str.contains("\""), "Error string shouldn't have quotes in it.")
327-
errors += [str]
328-
}
329-
330-
func toJSON() -> JSON {
331-
return .array(errors.map(JSON.string))
332-
}
333-
}
334-
335309
func manifestToJSON(_ package: Package) -> String {
336-
var dict: [String: JSON] = [:]
337-
dict["package"] = package.toJSON()
338-
dict["errors"] = errors.toJSON()
339-
return JSON.dictionary(dict).toString()
340-
}
310+
struct Output: Encodable {
311+
let package: Package
312+
let errors: [String]
313+
}
341314

342-
// FIXME: This function is public to let other targets access JSON string
343-
// representation of the package without exposing the enum JSON defined in this
344-
// target because that'll leak to clients of PackageDescription i.e every
345-
// Package.swift file.
346-
public func jsonString(package: Package) -> String {
347-
return package.toJSON().toString()
315+
let encoder = JSONEncoder()
316+
let data = try! encoder.encode(Output(package: package, errors: errors))
317+
return String(data: data, encoding: .utf8)!
348318
}
349319

350-
var errors = Errors()
320+
var errors: [String] = []
351321
private var dumpInfo: (package: Package, fileNo: Int32)?
352322
private func dumpPackageAtExit(_ package: Package, fileNo: Int32) {
353323
func dump() {
354324
guard let dumpInfo = dumpInfo else { return }
355-
let fd = fdopen(dumpInfo.fileNo, "w")
356-
guard fd != nil else { return }
325+
guard let fd = fdopen(dumpInfo.fileNo, "w") else { return }
357326
fputs(manifestToJSON(dumpInfo.package), fd)
358327
fclose(fd)
359328
}

0 commit comments

Comments
 (0)