Skip to content

Commit

Permalink
Fix JSON project description SPM compatibility
Browse files Browse the repository at this point in the history
Swift Package Manager uses `target_dependencies` instead of
`dependencies` to list module dependencies. This change also improves
the robustness when decoding JSON project descriptions for targets that
lack explicit sources or dependencies.
  • Loading branch information
kielgillard committed Jul 23, 2021
1 parent 6a01bb7 commit 201a6bb
Show file tree
Hide file tree
Showing 6 changed files with 415 additions and 1 deletion.
37 changes: 37 additions & 0 deletions Mockingbird.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,11 @@
287F852E25195011007D135D /* XcodeProj in Frameworks */ = {isa = PBXBuildFile; productRef = 287F852D25195011007D135D /* XcodeProj */; };
287F85322519659B007D135D /* MockingbirdGenerator.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = "Mockingbird::MockingbirdGenerator::Product" /* MockingbirdGenerator.framework */; };
28950E2D251C4C82008EEE29 /* ProjectDescription.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28950E2C251C4C82008EEE29 /* ProjectDescription.swift */; };
2896E54126AA52A400124D02 /* KeyedDecodingContainer+Array.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2896E54026AA52A400124D02 /* KeyedDecodingContainer+Array.swift */; };
2896E54626AA5A3E00124D02 /* spm-project-description.json in CopyFiles */ = {isa = PBXBuildFile; fileRef = 2896E54326AA59ED00124D02 /* spm-project-description.json */; };
2896E54726AA5A3F00124D02 /* generic-project-description.json in CopyFiles */ = {isa = PBXBuildFile; fileRef = 2896E54426AA5A1900124D02 /* generic-project-description.json */; };
28DAD96E251BDD66001A0B3F /* Project.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28DAD96D251BDD66001A0B3F /* Project.swift */; };
8356225C26A94CBE005CD5C5 /* TargetDescriptionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8356225B26A94CBE005CD5C5 /* TargetDescriptionTests.swift */; };
D314ED7F24CE1C10000CC23D /* GenericsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D314ED7E24CE1C10000CC23D /* GenericsTests.swift */; };
D3643B6C247B78A5002DF069 /* Function.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3643B6B247B78A4002DF069 /* Function.swift */; };
D3643B72247C5107002DF069 /* String+Components.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3643B71247C5107002DF069 /* String+Components.swift */; };
Expand Down Expand Up @@ -365,14 +369,32 @@
};
/* End PBXContainerItemProxy section */

/* Begin PBXCopyFilesBuildPhase section */
2896E54526AA5A2F00124D02 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 7;
files = (
2896E54626AA5A3E00124D02 /* spm-project-description.json in CopyFiles */,
2896E54726AA5A3F00124D02 /* generic-project-description.json in CopyFiles */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */

/* Begin PBXFileReference section */
281B61F1251DBBDE0084EBED /* ModuleNameShadowing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModuleNameShadowing.swift; sourceTree = "<group>"; };
281B6226251DC5AD0084EBED /* MockingbirdShadowedTestsHost.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MockingbirdShadowedTestsHost.framework; sourceTree = BUILT_PRODUCTS_DIR; };
281B62CF251DCAB90084EBED /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
281B62FE251DCCFC0084EBED /* MockingbirdShadowedTestsHostMocks.generated.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MockingbirdShadowedTestsHostMocks.generated.swift; path = Tests/MockingbirdTests/Mocks/MockingbirdShadowedTestsHostMocks.generated.swift; sourceTree = "<group>"; };
287F853D25196ACE007D135D /* Package.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Package.swift; path = Sources/Package.swift; sourceTree = "<group>"; };
28950E2C251C4C82008EEE29 /* ProjectDescription.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProjectDescription.swift; sourceTree = "<group>"; };
2896E54026AA52A400124D02 /* KeyedDecodingContainer+Array.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "KeyedDecodingContainer+Array.swift"; sourceTree = "<group>"; };
2896E54326AA59ED00124D02 /* spm-project-description.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "spm-project-description.json"; sourceTree = "<group>"; };
2896E54426AA5A1900124D02 /* generic-project-description.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "generic-project-description.json"; sourceTree = "<group>"; };
28DAD96D251BDD66001A0B3F /* Project.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Project.swift; sourceTree = "<group>"; };
8356225B26A94CBE005CD5C5 /* TargetDescriptionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TargetDescriptionTests.swift; sourceTree = "<group>"; };
942B00CDCB48ADC877A01AEE /* MockingbirdTestsHostMocks.generated.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; name = MockingbirdTestsHostMocks.generated.swift; path = Tests/MockingbirdTests/Mocks/MockingbirdTestsHostMocks.generated.swift; sourceTree = "<group>"; };
D314ED7E24CE1C10000CC23D /* GenericsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GenericsTests.swift; sourceTree = "<group>"; };
D3643B6B247B78A4002DF069 /* Function.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Function.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -742,6 +764,15 @@
name = Frameworks;
sourceTree = "<group>";
};
2896E54226AA59BE00124D02 /* Resources */ = {
isa = PBXGroup;
children = (
2896E54326AA59ED00124D02 /* spm-project-description.json */,
2896E54426AA5A1900124D02 /* generic-project-description.json */,
);
path = Resources;
sourceTree = "<group>";
};
C5115F86A66BC03B1665F801 /* Generated Mocks */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -829,6 +860,7 @@
OBJ_117 /* Array+Extensions.swift */,
OBJ_118 /* CharacterSet+Extensions.swift */,
OBJ_120 /* Deallocation.swift */,
2896E54026AA52A400124D02 /* KeyedDecodingContainer+Array.swift */,
OBJ_121 /* Log.swift */,
OBJ_122 /* OSLog+Extensions.swift */,
OBJ_123 /* OperationQueue+Extensions.swift */,
Expand Down Expand Up @@ -1141,12 +1173,14 @@
OBJ_272 /* Generator */ = {
isa = PBXGroup;
children = (
2896E54226AA59BE00124D02 /* Resources */,
OBJ_273 /* DeclaredTypeTests.swift */,
OBJ_274 /* InferTypeTests.swift */,
OBJ_275 /* PBXTargetTests.swift */,
OBJ_276 /* PathFnmatchTests.swift */,
OBJ_277 /* StringExtensionsTests.swift */,
OBJ_278 /* SubstitutionStyleTests.swift */,
8356225B26A94CBE005CD5C5 /* TargetDescriptionTests.swift */,
);
path = Generator;
sourceTree = "<group>";
Expand Down Expand Up @@ -1532,6 +1566,7 @@
02321D380F2F1A659EC139B7 /* Generate Mockingbird Mocks */,
OBJ_1018 /* Sources */,
OBJ_1083 /* Frameworks */,
2896E54526AA5A2F00124D02 /* CopyFiles */,
);
buildRules = (
);
Expand Down Expand Up @@ -1725,6 +1760,7 @@
OBJ_1048 /* SubscriptStubbableTests.swift in Sources */,
OBJ_1049 /* TypealiasingMockableTests.swift in Sources */,
OBJ_1050 /* TypealiasingStubbableTests.swift in Sources */,
8356225C26A94CBE005CD5C5 /* TargetDescriptionTests.swift in Sources */,
OBJ_1051 /* UndefinedArgumentLabelsMockableTests.swift in Sources */,
OBJ_1052 /* UndefinedArgumentLabelsStubbableTests.swift in Sources */,
OBJ_1053 /* VariablesMockableTests.swift in Sources */,
Expand Down Expand Up @@ -1932,6 +1968,7 @@
28DAD96E251BDD66001A0B3F /* Project.swift in Sources */,
OBJ_929 /* Typealias.swift in Sources */,
OBJ_930 /* Variable.swift in Sources */,
2896E54126AA52A400124D02 /* KeyedDecodingContainer+Array.swift in Sources */,
OBJ_931 /* BasicOperation.swift in Sources */,
OBJ_932 /* CheckCacheOperation.swift in Sources */,
OBJ_933 /* ExtractSourcesOperation.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public struct ProjectDescription: Codable, Hashable {
public let targets: [TargetDescription]
}

public struct TargetDescription: Codable, Hashable {
public struct TargetDescription: Hashable {
public let name: String
public let c99name: String?
public let type: String
Expand All @@ -25,6 +25,36 @@ public struct TargetDescription: Codable, Hashable {
}
}

extension TargetDescription: Codable {
public enum CodingKeys: String, CodingKey, CaseIterable {
case name
case c99name
case type
case path
case sources
case dependencies
}

/// Compatibility with Swift Package Manager JSON project descriptions.
enum SwiftPackageManagerKeys: String, CodingKey, CaseIterable {
case targetDependencies = "target_dependencies"
}

public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.name = try container.decode(String.self, forKey: .name)
self.c99name = try container.decodeIfPresent(String.self, forKey: .c99name)
self.type = try container.decode(String.self, forKey: .type)
self.path = try container.decode(Path.self, forKey: .path)
self.sources = try container.decodeIfPresent([Path].self, forKey: .sources) ?? []

let spmContainer = try decoder.container(keyedBy: SwiftPackageManagerKeys.self)
self.dependencies = try spmContainer.decodeIfPresent([String].self, forKey: .targetDependencies)
?? container.decodeIfPresent([String].self, forKey: .dependencies)
?? []
}
}

public enum TargetDescriptionType: String {
case library = "library"
case test = "test"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
//
// KeyedDecodingContainer+Array.swift
// MockingbirdGenerator
//
// Created by typealias on 7/22/21.
//

import Foundation

extension KeyedDecodingContainer {
/// Decodes a value of the given array type for the given key, if present.
///
/// This method returns `nil` if the container does not have a value
/// associated with `key`, or if the value is null. The difference between
/// these states can be distinguished with a `contains(_:)` call.
///
/// - parameter type: The type of value to decode.
/// - parameter key: The key that the decoded value is associated with.
/// - returns: A decoded value of the requested type, or `nil` if the
/// `Decoder` does not have an entry associated with the given key, or if
/// the value is a null value.
/// - throws: `DecodingError.typeMismatch` if the encountered encoded value
/// is not convertible to the requested type.
func decodeIfPresent<T: Decodable>(
_ type: [T].Type,
forKey key: KeyedDecodingContainer<K>.Key
) throws -> [T]? {
guard contains(key) else {
return nil
}

do {
var container = try nestedUnkeyedContainer(forKey: key)
var buffer = [T]()
buffer.reserveCapacity(container.count ?? 0)

while !container.isAtEnd {
buffer.append(try container.decode(T.self))
}
return buffer
} catch DecodingError.valueNotFound {
return nil
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"targets":[
{
"c99name":"FeatureTargetTests",
"module_type":"SwiftTarget",
"name":"FeatureTargetTests",
"path":"Tests/FeatureTargetTests",
"sources":[
"ModelTests.swift",
"ControllerTests.swift",
"ViewTests.swift"
],
"dependencies":[
"FeatureTarget"
],
"type":"test"
},
{
"c99name":"FeatureTarget",
"module_type":"SwiftTarget",
"name":"FeatureTarget",
"path":"Sources/FeatureTarget",
"sources":[
"Models/Things.swift",
"Controllers/MasterViewController.swift",
"Controllers/DetailViewController.swift",
"Views/DetailView.swift"
],
"dependencies":[],
"type":"library"
},
{
"c99name":"EmptyTarget",
"module_type":"SwiftTarget",
"name":"EmptyTarget",
"path":"Sources/EmptyTarget",
"type":"library"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
{
"dependencies":[
{
"requirement":{
"local_package":null
},
"url":"../path/to/local/package"
},
{
"name":"Mockingbird",
"requirement":{
"range":[
{
"lower_bound":"0.16.0",
"upper_bound":"0.17.0"
}
]
},
"url":"https://github.com/birdrides/mockingbird.git"
}
],
"name":"FeaturePackage",
"path":"/path/to/FeaturePackage",
"platforms":[
{
"name":"ios",
"version":"12.0"
}
],
"products":[
{
"name":"Feature",
"targets":[
"FeatureTarget"
],
"type":{
"library":[
"automatic"
]
}
}
],
"targets":[
{
"c99name":"FeatureTargetTests",
"module_type":"SwiftTarget",
"name":"FeatureTargetTests",
"path":"Tests/FeatureTargetTests",
"sources":[
"ModelTests.swift",
"ControllerTests.swift",
"ViewTests.swift"
],
"target_dependencies":[
"FeatureTarget"
],
"type":"test"
},
{
"c99name":"FeatureTarget",
"module_type":"SwiftTarget",
"name":"FeatureTarget",
"path":"Sources/FeatureTarget",
"product_memberships":[
"Feature"
],
"resources":[
{
"path":"/path/to/FeatureTarget.storyboard",
"rule":"process"
}
],
"sources":[
"Models/Things.swift",
"Controllers/MasterViewController.swift",
"Controllers/DetailViewController.swift",
"Views/DetailView.swift"
],
"target_dependencies":[

],
"type":"library"
},
{
"c99name":"EmptyTarget",
"module_type":"SwiftTarget",
"name":"EmptyTarget",
"path":"Sources/EmptyTarget",
"product_memberships":[
"Feature"
],
"type":"library"
}
],
"tools_version":"5.3"
}

0 comments on commit 201a6bb

Please sign in to comment.