Enable adding local Swift packages to the project root (#1413)

* Enable adding local Swift packages to the project root

* Update CHANGELOG.md
This commit is contained in:
Hilton Campbell 2024-04-07 06:18:24 -07:00 committed by GitHub
parent 1645d419ce
commit 632ca2d419
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 81 additions and 5 deletions

View File

@ -2,6 +2,10 @@
## Next Version
### Added
- Added support for local Swift packages at the project root #1413 @hiltonc
## 2.39.1
### Added

View File

@ -147,7 +147,7 @@ Note that target names can also be changed by adding a `name` property to a targ
- [ ] **transitivelyLinkDependencies**: **Bool** - If this is `true` then targets will link to the dependencies of their target dependencies. If a target should embed its dependencies, such as application and test bundles, it will embed these transitive dependencies as well. Some complex setups might want to set this to `false` and explicitly specify dependencies at every level. Targets can override this with [Target](#target).transitivelyLinkDependencies. Defaults to `false`.
- [ ] **generateEmptyDirectories**: **Bool** - If this is `true` then empty directories will be added to project too else will be missed. Defaults to `false`.
- [ ] **findCarthageFrameworks**: **Bool** - When this is set to `true`, all the individual frameworks for Carthage framework dependencies will automatically be found. This property can be overridden individually for each carthage dependency - for more details see See **findFrameworks** in the [Dependency](#dependency) section. Defaults to `false`.
- [ ] **localPackagesGroup**: **String** - The group name that local packages are put into. This defaults to `Packages`
- [ ] **localPackagesGroup**: **String** - The group name that local packages are put into. This defaults to `Packages`. Use `""` to specify the project root.
- [ ] **fileTypes**: **[String: [FileType](#filetype)]** - A list of default file options for specific file extensions across the project. Values in [Sources](#sources) will overwrite these settings.
- [ ] **preGenCommand**: **String** - A bash command to run before the project has been generated. If the project isn't generated due to no changes when using the cache then this won't run. This is useful for running things like generating resources files before the project is regenerated.
- [ ] **postGenCommand**: **String** - A bash command to run after the project has been generated. If the project isn't generated due to no changes when using the cache then this won't run. This is useful for running things like `pod install` only if the project is actually regenerated.
@ -1245,7 +1245,7 @@ Swift packages are defined at a project level, and then linked to individual tar
### Local Package
- [x] **path**: **String** - the path to the package in local. The path must be directory with a `Package.swift`.
- [ ] **group** : **String**- Optional path that specifies the location where the package will live in your xcode project.
- [ ] **group** : **String**- Optional path that specifies the location where the package will live in your xcode project. Use `""` to specify the project root.
```yml
packages:

View File

@ -59,7 +59,7 @@ class SourceGenerator {
}
let absolutePath = project.basePath + path.normalize()
// Get the local package's relative path from the project root
let fileReferencePath = try? absolutePath.relativePath(from: projectDirectory ?? project.basePath).string
@ -72,8 +72,12 @@ class SourceGenerator {
)
)
let parentGroups = parentGroup.components(separatedBy: "/")
createParentGroups(parentGroups, for: fileReference)
if parentGroup == "" {
rootGroups.insert(fileReference)
} else {
let parentGroups = parentGroup.components(separatedBy: "/")
createParentGroups(parentGroups, for: fileReference)
}
}
/// Collects an array complete of all `SourceFile` objects that make up the target based on the provided `TargetSource` definitions.

View File

@ -1642,6 +1642,74 @@ class ProjectGeneratorTests: XCTestCase {
try expect(file.product?.productName) == "XcodeGen"
}
$0.it("generates local swift packages at the top level") {
let app = Target(
name: "MyApp",
type: .application,
platform: .iOS,
dependencies: [
Dependency(type: .package(products: []), reference: "XcodeGen"),
]
)
let project = Project(name: "test", targets: [app], packages: ["XcodeGen": .local(path: "../XcodeGen", group: "")])
let pbxProject = try project.generatePbxProj(specValidate: false)
let nativeTarget = try unwrap(pbxProject.nativeTargets.first(where: { $0.name == app.name }))
let localPackageFile = try unwrap(pbxProject.fileReferences.first(where: { $0.path == "../XcodeGen" }))
try expect(localPackageFile.lastKnownFileType) == "folder"
let mainGroup = try pbxProject.getMainGroup()
try expect(mainGroup.children.contains(localPackageFile)) == true
let frameworkPhases = nativeTarget.buildPhases.compactMap { $0 as? PBXFrameworksBuildPhase }
guard let frameworkPhase = frameworkPhases.first else {
return XCTFail("frameworkPhases should have more than one")
}
guard let file = frameworkPhase.files?.first else {
return XCTFail("frameworkPhase should have file")
}
try expect(file.product?.productName) == "XcodeGen"
}
$0.it("generates local swift package group at the top level") {
let app = Target(
name: "MyApp",
type: .application,
platform: .iOS,
dependencies: [
Dependency(type: .package(products: []), reference: "XcodeGen"),
]
)
let project = Project(name: "test", targets: [app], packages: ["XcodeGen": .local(path: "../XcodeGen", group: nil)], options: .init(localPackagesGroup: ""))
let pbxProject = try project.generatePbxProj(specValidate: false)
let nativeTarget = try unwrap(pbxProject.nativeTargets.first(where: { $0.name == app.name }))
let localPackageFile = try unwrap(pbxProject.fileReferences.first(where: { $0.path == "../XcodeGen" }))
try expect(localPackageFile.lastKnownFileType) == "folder"
let mainGroup = try pbxProject.getMainGroup()
try expect(mainGroup.children.contains(localPackageFile)) == true
let frameworkPhases = nativeTarget.buildPhases.compactMap { $0 as? PBXFrameworksBuildPhase }
guard let frameworkPhase = frameworkPhases.first else {
return XCTFail("frameworkPhases should have more than one")
}
guard let file = frameworkPhase.files?.first else {
return XCTFail("frameworkPhase should have file")
}
try expect(file.product?.productName) == "XcodeGen"
}
$0.it("generates info.plist") {
let plist = Plist(path: "Info.plist", attributes: ["UISupportedInterfaceOrientations": ["UIInterfaceOrientationPortrait", "UIInterfaceOrientationLandscapeLeft"]])
let tempPath = Path.temporary + "info"