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

Supports specifying multiple package products #1395

Merged
merged 7 commits into from
Sep 11, 2023
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
### Fixed

- Fixed source file `includes` not working when no paths were found #1337 @shnhrrsn
- Supports specifying multiple package products #1395 @simonbs

## 2.37.0

Expand Down
18 changes: 17 additions & 1 deletion Docs/ProjectSpec.md
Original file line number Diff line number Diff line change
Expand Up @@ -645,7 +645,8 @@ targets:
```

**Package dependency**
- [ ] **product**: **String** - The product to use from the package. This defaults to the package name, so is only required if a Package has multiple libraries or a library with a differing name
- [ ] **product**: **String** - The product to use from the package. This defaults to the package name, so is only required if a Package has multiple libraries or a library with a differing name. Use this over `products` when you want to define different linking options per product.
- [ ] **products**: **String** - A list of products to use from the package. This can be used when depending on multiple products from a package.

```yaml
packages:
Expand All @@ -663,6 +664,21 @@ targets:
product: SPMUtility
```

Depending on multiple products from a package:

```yaml
packages:
FooFeature:
path: Packages/FooFeature
targets:
App:
dependencies:
- package: FooFeature
products:
- FooDomain
- FooUI
Comment on lines +675 to +679
Copy link
Collaborator

Choose a reason for hiding this comment

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

How can we handle if we want to do below case?

  • FooDomain: embed = true
  • FooUI: embed = false

Copy link
Contributor Author

Choose a reason for hiding this comment

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

My thinking was that people would use product when needing fine-grained control and products (with an s) when products are to be added the same way.

dependencies:
- package: FooFeature
  product: FooDomain
  embed: true
- package: FooFeature
  product: FooUI
  embed: false

Alternatively, products should be objects as shown below while also supporting products as plain strings. If both aren't supported, the benefit of products become too little.

So both of these would be valid:

dependencies:
- package: FooFeature
  products:
  - name: FooDomain
    embed: true
  - name: FooUI
    embed: false
dependencies:
- package: FooFeature
  products:
  - FooDomain
  - FooUI

Copy link
Owner

Choose a reason for hiding this comment

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

@simonbs the embed property is part of the Dependency type, so the list of objects would break that relationship.
I think having to split out into seperate product is fine. Would like to see that perhaps mentioned in the docs under product like "Useful if you want to define different linking options per product. "

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@yonaskolb That's a good idea. I've added it in 9785749.

```

### Config Files

Specifies `.xcconfig` files for each configuration.
Expand Down
21 changes: 14 additions & 7 deletions Sources/ProjectSpec/Dependency.swift
Original file line number Diff line number Diff line change
Expand Up @@ -61,17 +61,17 @@ public struct Dependency: Equatable {
case framework
case carthage(findFrameworks: Bool?, linkType: CarthageLinkType)
case sdk(root: String?)
case package(product: String?)
case package(products: [String])
case bundle
}
}

extension Dependency {
public var uniqueID: String {
switch type {
case .package(let product):
if let product = product {
return "\(reference)/\(product)"
case .package(let products):
if !products.isEmpty {
return "\(reference)/\(products.joined(separator: ","))"
} else {
return reference
}
Expand Down Expand Up @@ -106,9 +106,16 @@ extension Dependency: JSONObjectConvertible {
type = .sdk(root: sdkRoot)
reference = sdk
} else if let package: String = jsonDictionary.json(atKeyPath: "package") {
let product: String? = jsonDictionary.json(atKeyPath: "product")
type = .package(product: product)
reference = package
if let products: [String] = jsonDictionary.json(atKeyPath: "products") {
type = .package(products: products)
reference = package
} else if let product: String = jsonDictionary.json(atKeyPath: "product") {
type = .package(products: [product])
reference = package
} else {
type = .package(products: [])
reference = package
}
} else if let bundle: String = jsonDictionary.json(atKeyPath: "bundle") {
type = .bundle
reference = bundle
Expand Down
69 changes: 39 additions & 30 deletions Sources/XcodeGenKit/PBXProjGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -931,7 +931,7 @@ public class PBXProjGenerator {
}
}
// Embedding handled by iterating over `carthageDependencies` below
case .package(let product):
case .package(let products):
let packageReference = packageReferences[dependency.reference]

// If package's reference is none and there is no specified package in localPackages,
Expand All @@ -940,40 +940,49 @@ public class PBXProjGenerator {
continue
}

let productName = product ?? dependency.reference
let packageDependency = addObject(
XCSwiftPackageProductDependency(productName: productName, package: packageReference)
)
func addPackageProductDependency(named productName: String) {
let packageDependency = addObject(
XCSwiftPackageProductDependency(productName: productName, package: packageReference)
)

// Add package dependency if linking is true.
if dependency.link ?? true {
packageDependencies.append(packageDependency)
}
// Add package dependency if linking is true.
if dependency.link ?? true {
packageDependencies.append(packageDependency)
}

let link = dependency.link ?? (target.type != .staticLibrary)
if link {
let file = PBXBuildFile(product: packageDependency, settings: getDependencyFrameworkSettings(dependency: dependency))
file.platformFilter = platform
let buildFile = addObject(file)
targetFrameworkBuildFiles.append(buildFile)
} else {
let targetDependency = addObject(
PBXTargetDependency(platformFilter: platform, product: packageDependency)
)
dependencies.append(targetDependency)
let link = dependency.link ?? (target.type != .staticLibrary)
if link {
let file = PBXBuildFile(product: packageDependency, settings: getDependencyFrameworkSettings(dependency: dependency))
file.platformFilter = platform
let buildFile = addObject(file)
targetFrameworkBuildFiles.append(buildFile)
} else {
let targetDependency = addObject(
PBXTargetDependency(platformFilter: platform, product: packageDependency)
)
dependencies.append(targetDependency)
}

if dependency.embed == true {
let pbxBuildFile = PBXBuildFile(product: packageDependency,
settings: getEmbedSettings(dependency: dependency, codeSign: dependency.codeSign ?? true))
pbxBuildFile.platformFilter = platform
let embedFile = addObject(pbxBuildFile)

if dependency.copyPhase != nil {
customCopyDependenciesReferences.append(embedFile)
} else {
copyFrameworksReferences.append(embedFile)
}
}
}

if dependency.embed == true {
let pbxBuildFile = PBXBuildFile(product: packageDependency,
settings: getEmbedSettings(dependency: dependency, codeSign: dependency.codeSign ?? true))
pbxBuildFile.platformFilter = platform
let embedFile = addObject(pbxBuildFile)

if dependency.copyPhase != nil {
customCopyDependenciesReferences.append(embedFile)
} else {
copyFrameworksReferences.append(embedFile)
if !products.isEmpty {
for product in products {
addPackageProductDependency(named: product)
}
} else {
addPackageProductDependency(named: dependency.reference)
}
case .bundle:
// Static and dynamic libraries can't copy resources
Expand Down
20 changes: 20 additions & 0 deletions Tests/Fixtures/SPM/FooFeature/Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// swift-tools-version: 5.8
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
name: "FooFeature",
products: [
.library(name: "FooDomain", targets: [
"FooDomain"
]),
.library(name: "FooUI", targets: [
"FooUI"
])
],
targets: [
.target(name: "FooDomain"),
.target(name: "FooUI")
]
)
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
public struct FooDomain {}
1 change: 1 addition & 0 deletions Tests/Fixtures/SPM/FooFeature/Sources/FooUI/FooUI.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
public struct FooUI {}
44 changes: 44 additions & 0 deletions Tests/Fixtures/SPM/SPM.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,12 @@
/* Begin PBXBuildFile section */
23C6626698DE560017A89F2F /* XcodeGen in Frameworks */ = {isa = PBXBuildFile; productRef = 6F7DEA2D82649EDF903FBDBD /* XcodeGen */; };
2DA7998902987953B119E4CE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26F7EFEE613987D1E1258A60 /* AppDelegate.swift */; };
36CE2E6187D9709BAD9EF807 /* FooUI in Frameworks */ = {isa = PBXBuildFile; productRef = 927CB19D94339CC9960E930C /* FooUI */; };
3986ED6965842721C46C0452 /* SwiftRoaringDynamic in Frameworks */ = {isa = PBXBuildFile; productRef = DC73B269C8B0C0BF6912842C /* SwiftRoaringDynamic */; };
4CC663B42B270404EF5FD037 /* libStaticLibrary.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CAB5625F6FEA668410ED5482 /* libStaticLibrary.a */; };
9AD886A88D3E4A1B5E900687 /* SPMTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7970A2253B14A9B27C307FAC /* SPMTests.swift */; };
9C4AD0711D706FD3ED0E436D /* StaticLibrary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61C17B77601A9D1B7895AB42 /* StaticLibrary.swift */; };
AF8E362713B9D28EA9A5C9FC /* FooDomain in Frameworks */ = {isa = PBXBuildFile; productRef = 8D2DC638BEF7FDF23907E134 /* FooDomain */; };
B89EA0F3859878A1DCF7BAFD /* SwiftRoaringDynamic in Embed Frameworks */ = {isa = PBXBuildFile; productRef = DC73B269C8B0C0BF6912842C /* SwiftRoaringDynamic */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
CE46CBA5671B951B546C8673 /* Codability in Frameworks */ = {isa = PBXBuildFile; productRef = 16E6FE01D5BD99F78D4A17E2 /* Codability */; settings = {ATTRIBUTES = (Weak, ); }; };
E368431019ABC696E4FFC0CF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4E22B8BCC18A29EFE1DE3BE4 /* Assets.xcassets */; };
Expand Down Expand Up @@ -71,6 +73,7 @@
4E22B8BCC18A29EFE1DE3BE4 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
61C17B77601A9D1B7895AB42 /* StaticLibrary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StaticLibrary.swift; sourceTree = "<group>"; };
7970A2253B14A9B27C307FAC /* SPMTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SPMTests.swift; sourceTree = "<group>"; };
979AE1767E2AF6B3B9D7F13D /* FooFeature */ = {isa = PBXFileReference; lastKnownFileType = folder; name = FooFeature; path = FooFeature; sourceTree = SOURCE_ROOT; };
A9601593D0AD02931266A4E5 /* App.xctestplan */ = {isa = PBXFileReference; path = App.xctestplan; sourceTree = "<group>"; };
CAB5625F6FEA668410ED5482 /* libStaticLibrary.a */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = archive.ar; path = libStaticLibrary.a; sourceTree = BUILT_PRODUCTS_DIR; };
ED284AB7C13DCC0A95DAA680 /* XcodeGen */ = {isa = PBXFileReference; lastKnownFileType = folder; name = XcodeGen; path = ../../..; sourceTree = SOURCE_ROOT; };
Expand All @@ -85,6 +88,8 @@
3986ED6965842721C46C0452 /* SwiftRoaringDynamic in Frameworks */,
4CC663B42B270404EF5FD037 /* libStaticLibrary.a in Frameworks */,
23C6626698DE560017A89F2F /* XcodeGen in Frameworks */,
AF8E362713B9D28EA9A5C9FC /* FooDomain in Frameworks */,
36CE2E6187D9709BAD9EF807 /* FooUI in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -114,6 +119,7 @@
218F6C96DF9E182F526258CF = {
isa = PBXGroup;
children = (
AD0F3623091EEA8D1EA3DFF8 /* Packages */,
17DD374CC81D710476AFF41C /* SPM */,
CF3BD77AEAA56553289456BA /* SPMTests */,
1FA59BFD192FB5A68D5F587C /* StaticLibrary */,
Expand All @@ -131,6 +137,14 @@
name = Products;
sourceTree = "<group>";
};
AD0F3623091EEA8D1EA3DFF8 /* Packages */ = {
isa = PBXGroup;
children = (
979AE1767E2AF6B3B9D7F13D /* FooFeature */,
);
name = Packages;
sourceTree = SOURCE_ROOT;
};
CF3BD77AEAA56553289456BA /* SPMTests */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -171,12 +185,16 @@
D85FFB99444DD260A72DDDA7 /* PBXTargetDependency */,
DDD17C561AD5065DF4FA4072 /* PBXTargetDependency */,
C6360997FFC102F6725099D4 /* PBXTargetDependency */,
00B467060F3DEC027711F9C2 /* PBXTargetDependency */,
7EB17E90A4D8F26FEABEEDF6 /* PBXTargetDependency */,
);
name = StaticLibrary;
packageProductDependencies = (
AF233B61592982A7F6431FC6 /* Codability */,
C816AEB28ED71C3C47F31B98 /* SwiftRoaringDynamic */,
5A36E2FE69703FCAC0BE8064 /* XcodeGen */,
6B8A6E1EA485E607A1D1DCD1 /* FooDomain */,
15DB49096E2978F6BCA8D604 /* FooUI */,
);
productName = StaticLibrary;
productReference = CAB5625F6FEA668410ED5482 /* libStaticLibrary.a */;
Expand All @@ -202,6 +220,8 @@
16E6FE01D5BD99F78D4A17E2 /* Codability */,
DC73B269C8B0C0BF6912842C /* SwiftRoaringDynamic */,
6F7DEA2D82649EDF903FBDBD /* XcodeGen */,
8D2DC638BEF7FDF23907E134 /* FooDomain */,
927CB19D94339CC9960E930C /* FooUI */,
);
productName = App;
productReference = 097F2DB5622B591E21BC3C73 /* App.app */;
Expand Down Expand Up @@ -301,11 +321,19 @@
/* End PBXSourcesBuildPhase section */

/* Begin PBXTargetDependency section */
00B467060F3DEC027711F9C2 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
productRef = 6B8A6E1EA485E607A1D1DCD1 /* FooDomain */;
};
078202CF7B08B66ACF7FEC23 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 3F8D94C4EFC431F646AAFB28 /* StaticLibrary */;
targetProxy = 29147E1DDAEB1AAC20CB0CF9 /* PBXContainerItemProxy */;
};
7EB17E90A4D8F26FEABEEDF6 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
productRef = 15DB49096E2978F6BCA8D604 /* FooUI */;
};
8693351DA9DBE579AC9DD513 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = C99E3C420D63D5219CE57E33 /* App */;
Expand Down Expand Up @@ -635,6 +663,10 @@
/* End XCRemoteSwiftPackageReference section */

/* Begin XCSwiftPackageProductDependency section */
15DB49096E2978F6BCA8D604 /* FooUI */ = {
isa = XCSwiftPackageProductDependency;
productName = FooUI;
};
16E6FE01D5BD99F78D4A17E2 /* Codability */ = {
isa = XCSwiftPackageProductDependency;
package = 5BA91390AE78D2EE15C60091 /* XCRemoteSwiftPackageReference "Codability" */;
Expand All @@ -644,6 +676,10 @@
isa = XCSwiftPackageProductDependency;
productName = XcodeGen;
};
6B8A6E1EA485E607A1D1DCD1 /* FooDomain */ = {
isa = XCSwiftPackageProductDependency;
productName = FooDomain;
};
6F7DEA2D82649EDF903FBDBD /* XcodeGen */ = {
isa = XCSwiftPackageProductDependency;
productName = XcodeGen;
Expand All @@ -653,6 +689,14 @@
package = 348C81C327DB1710B742C370 /* XCRemoteSwiftPackageReference "Prefire" */;
productName = "plugin:PrefirePlaybookPlugin";
};
8D2DC638BEF7FDF23907E134 /* FooDomain */ = {
isa = XCSwiftPackageProductDependency;
productName = FooDomain;
};
927CB19D94339CC9960E930C /* FooUI */ = {
isa = XCSwiftPackageProductDependency;
productName = FooUI;
};
AF233B61592982A7F6431FC6 /* Codability */ = {
isa = XCSwiftPackageProductDependency;
package = 5BA91390AE78D2EE15C60091 /* XCRemoteSwiftPackageReference "Codability" */;
Expand Down
10 changes: 10 additions & 0 deletions Tests/Fixtures/SPM/project.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ packages:
XcodeGen:
path: ../../.. #XcodeGen itself
group: SPM
FooFeature:
path: FooFeature
aggregateTargets:
AggTarget:
buildToolPlugins:
Expand All @@ -37,6 +39,10 @@ targets:
embed: true
- target: StaticLibrary
- package: XcodeGen
- package: FooFeature
products:
- FooDomain
- FooUI
Tests:
type: bundle.unit-test
platform: iOS
Expand All @@ -52,3 +58,7 @@ targets:
- package: SwiftRoaring
product: SwiftRoaringDynamic
- package: XcodeGen
- package: FooFeature
products:
- FooDomain
- FooUI
6 changes: 3 additions & 3 deletions Tests/ProjectSpecTests/ProjectSpecTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,8 @@ class ProjectSpecTests: XCTestCase {
Dependency(type: .framework, reference: "dependency2"),

// multiple package dependencies with different products should be allowed
Dependency(type: .package(product: "one"), reference: "package1"),
Dependency(type: .package(product: "two"), reference: "package1"),
Dependency(type: .package(products: ["one"]), reference: "package1"),
Dependency(type: .package(products: ["two"]), reference: "package1"),
]
),
Target(
Expand Down Expand Up @@ -205,7 +205,7 @@ class ProjectSpecTests: XCTestCase {
sources: ["invalidSource"],
dependencies: [
Dependency(type: .target, reference: "invalidDependency"),
Dependency(type: .package(product: nil), reference: "invalidPackage"),
Dependency(type: .package(products: []), reference: "invalidPackage"),
],
preBuildScripts: [BuildScript(script: .path("invalidPreBuildScript"), name: "preBuildScript1")],
postCompileScripts: [BuildScript(script: .path("invalidPostCompileScript"))],
Expand Down
Loading