Skip to content

Commit

Permalink
Enable adding local Swift packages to the project root (#1413)
Browse files Browse the repository at this point in the history
* Enable adding local Swift packages to the project root

* Update CHANGELOG.md
  • Loading branch information
hiltonc authored Apr 7, 2024
1 parent 1645d41 commit 632ca2d
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 5 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Next Version

### Added

- Added support for local Swift packages at the project root #1413 @hiltonc

## 2.39.1

### Added
Expand Down
4 changes: 2 additions & 2 deletions Docs/ProjectSpec.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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:
Expand Down
10 changes: 7 additions & 3 deletions Sources/XcodeGenKit/SourceGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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.
Expand Down
68 changes: 68 additions & 0 deletions Tests/XcodeGenKitTests/ProjectGeneratorTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down

0 comments on commit 632ca2d

Please sign in to comment.