Added ability to use custom location for local Swift packages (#1175)

* Added xcodePath functionality

* Added xcodePath functionality

* Renamed Xcode path to group

* Updated change log and added fixture tests
This commit is contained in:
John Connolly
2022-03-24 17:26:06 -07:00
committed by GitHub
parent 5350e26cd5
commit be0c3c3926
15 changed files with 90 additions and 36 deletions
+3
View File
@@ -2,6 +2,9 @@
## Next Version
#### Added
- Support for specifying custom group locations for SPM packages. [#1173](https://github.com/yonaskolb/XcodeGen/issues/1173) @John-Connolly
### Fixed
- Fix Monterey macOS shell version, shell login flag for environments [#1167](https://github.com/yonaskolb/XcodeGen/issues/1167) @bimawa
+4
View File
@@ -993,6 +993,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.
```yml
packages:
@@ -1004,6 +1005,9 @@ packages:
from: 0.5.0
RxClient:
path: ../RxClient
AppFeature:
path: ../Packages
group: Domains/AppFeature
```
## Project Reference
+1 -1
View File
@@ -193,7 +193,7 @@ extension Project {
packages.merge(localPackages.reduce(into: [String: SwiftPackage]()) {
// Project name will be obtained by resolved abstractpath's lastComponent for dealing with some path case, like "../"
let packageName = (basePath + Path($1).normalize()).lastComponent
$0[packageName] = .local(path: $1)
$0[packageName] = .local(path: $1, group: nil)
}
)
}
+1 -1
View File
@@ -54,7 +54,7 @@ extension Project {
}
for (name, package) in packages {
if case let .local(path) = package, !(basePath + Path(path).normalize()).exists {
if case let .local(path, _) = package, !(basePath + Path(path).normalize()).exists {
errors.append(.invalidLocalPackage(name))
}
}
+7 -4
View File
@@ -10,7 +10,7 @@ public enum SwiftPackage: Equatable {
static let githubPrefix = "https://github.com/"
case remote(url: String, versionRequirement: VersionRequirement)
case local(path: String)
case local(path: String, group: String?)
public var isLocal: Bool {
if case .local = self {
@@ -23,8 +23,10 @@ public enum SwiftPackage: Equatable {
extension SwiftPackage: JSONObjectConvertible {
public init(jsonDictionary: JSONDictionary) throws {
if let path: String = jsonDictionary.json(atKeyPath: "path") {
self = .local(path: path)
if let path: String = jsonDictionary.json(atKeyPath: "path"), let customLocation: String = jsonDictionary.json(atKeyPath: "group") {
self = .local(path: path, group: customLocation)
} else if let path: String = jsonDictionary.json(atKeyPath: "path") {
self = .local(path: path, group: nil)
} else {
let versionRequirement: VersionRequirement = try VersionRequirement(jsonDictionary: jsonDictionary)
try Self.validateVersion(versionRequirement: versionRequirement)
@@ -90,8 +92,9 @@ extension SwiftPackage: JSONEncodable {
dictionary["revision"] = revision
}
return dictionary
case .local(let path):
case let .local(path, group):
dictionary["path"] = path
dictionary["group"] = group
}
return dictionary
@@ -28,9 +28,9 @@ class ProjectCommand: Command {
}
func execute() throws {
let projectSpecPath = (spec ?? "project.yml").absolute()
if !projectSpecPath.exists {
throw GenerationError.missingProjectSpec(projectSpecPath)
}
+2 -2
View File
@@ -168,8 +168,8 @@ public class PBXProjGenerator {
let packageReference = XCRemoteSwiftPackageReference(repositoryURL: url, versionRequirement: versionRequirement)
packageReferences[name] = packageReference
addObject(packageReference)
case let .local(path):
try sourceGenerator.createLocalPackage(path: Path(path))
case let .local(path, group):
try sourceGenerator.createLocalPackage(path: Path(path), group: group.map { Path($0) })
}
}
+1 -1
View File
@@ -141,7 +141,7 @@ public class SchemeGenerator {
switch target.location {
case .package(let packageName):
guard let package = self.project.getPackage(packageName),
case .local(let path) = package else {
case let .local(path, _) = package else {
throw SchemeGenerationError.missingPackage(packageName)
}
return XCScheme.BuildableReference(
+16 -6
View File
@@ -53,16 +53,22 @@ class SourceGenerator {
return object
}
func createLocalPackage(path: Path) throws {
if localPackageGroup == nil {
func createLocalPackage(path: Path, group: Path?) throws {
var pbxGroup: PBXGroup?
if let location = group {
let fullLocationPath = project.basePath + location
pbxGroup = getGroup(path: fullLocationPath, mergingChildren: [], createIntermediateGroups: true, hasCustomParent: false, isBaseGroup: true)
}
if localPackageGroup == nil && group == nil {
let groupName = project.options.localPackagesGroup ?? "Packages"
localPackageGroup = addObject(PBXGroup(sourceTree: .sourceRoot, name: groupName))
rootGroups.insert(localPackageGroup!)
}
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
@@ -74,7 +80,11 @@ class SourceGenerator {
path: fileReferencePath
)
)
localPackageGroup!.children.append(fileReference)
if let pbxGroup = pbxGroup {
pbxGroup.children.append(fileReference)
} else {
localPackageGroup!.children.append(fileReference)
}
}
/// Collects an array complete of all `SourceFile` objects that make up the target based on the provided `TargetSource` definitions.
@@ -7,6 +7,7 @@
objects = {
/* Begin PBXBuildFile section */
23C6626698DE560017A89F2F /* XcodeGen in Frameworks */ = {isa = PBXBuildFile; productRef = 6F7DEA2D82649EDF903FBDBD /* XcodeGen */; };
2DA7998902987953B119E4CE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26F7EFEE613987D1E1258A60 /* AppDelegate.swift */; };
3986ED6965842721C46C0452 /* SwiftRoaringDynamic in Frameworks */ = {isa = PBXBuildFile; productRef = DC73B269C8B0C0BF6912842C /* SwiftRoaringDynamic */; };
4CC663B42B270404EF5FD037 /* libStaticLibrary.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CAB5625F6FEA668410ED5482 /* libStaticLibrary.a */; };
@@ -58,8 +59,8 @@
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>"; };
A9601593D0AD02931266A4E5 /* App.xctestplan */ = {isa = PBXFileReference; path = App.xctestplan; sourceTree = "<group>"; };
C1DE9A872F470EAA65B9B0B0 /* XcodeGen */ = {isa = PBXFileReference; lastKnownFileType = folder; name = XcodeGen; path = ../../..; sourceTree = SOURCE_ROOT; };
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; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -70,6 +71,7 @@
CE46CBA5671B951B546C8673 /* Codability in Frameworks */,
3986ED6965842721C46C0452 /* SwiftRoaringDynamic in Frameworks */,
4CC663B42B270404EF5FD037 /* libStaticLibrary.a in Frameworks */,
23C6626698DE560017A89F2F /* XcodeGen in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -83,6 +85,7 @@
26F7EFEE613987D1E1258A60 /* AppDelegate.swift */,
4E22B8BCC18A29EFE1DE3BE4 /* Assets.xcassets */,
464ACF8D8F2D9F219BCFD3E7 /* Info.plist */,
ED284AB7C13DCC0A95DAA680 /* XcodeGen */,
);
path = SPM;
sourceTree = "<group>";
@@ -98,7 +101,6 @@
218F6C96DF9E182F526258CF = {
isa = PBXGroup;
children = (
AD0F3623091EEA8D1EA3DFF8 /* Packages */,
17DD374CC81D710476AFF41C /* SPM */,
CF3BD77AEAA56553289456BA /* SPMTests */,
1FA59BFD192FB5A68D5F587C /* StaticLibrary */,
@@ -116,14 +118,6 @@
name = Products;
sourceTree = "<group>";
};
AD0F3623091EEA8D1EA3DFF8 /* Packages */ = {
isa = PBXGroup;
children = (
C1DE9A872F470EAA65B9B0B0 /* XcodeGen */,
);
name = Packages;
sourceTree = SOURCE_ROOT;
};
CF3BD77AEAA56553289456BA /* SPMTests */ = {
isa = PBXGroup;
children = (
@@ -193,6 +187,7 @@
packageProductDependencies = (
16E6FE01D5BD99F78D4A17E2 /* Codability */,
DC73B269C8B0C0BF6912842C /* SwiftRoaringDynamic */,
6F7DEA2D82649EDF903FBDBD /* XcodeGen */,
);
productName = App;
productReference = 097F2DB5622B591E21BC3C73 /* App.app */;
@@ -596,6 +591,10 @@
isa = XCSwiftPackageProductDependency;
productName = XcodeGen;
};
6F7DEA2D82649EDF903FBDBD /* XcodeGen */ = {
isa = XCSwiftPackageProductDependency;
productName = XcodeGen;
};
AF233B61592982A7F6431FC6 /* Codability */ = {
isa = XCSwiftPackageProductDependency;
package = 5BA91390AE78D2EE15C60091 /* XCRemoteSwiftPackageReference "Codability" */;
+2
View File
@@ -8,6 +8,7 @@ packages:
majorVersion: 1.0.4
XcodeGen:
path: ../../.. #XcodeGen itself
group: SPM
targets:
App:
type: application
@@ -24,6 +25,7 @@ targets:
product: SwiftRoaringDynamic
embed: true
- target: StaticLibrary
- package: XcodeGen
Tests:
type: bundle.unit-test
platform: iOS
@@ -126,7 +126,7 @@ class ProjectSpecTests: XCTestCase {
project.settings = invalidSettings
project.configFiles = ["invalidConfig": "invalidConfigFile"]
project.fileGroups = ["invalidFileGroup"]
project.packages = ["invalidLocalPackage": .local(path: "invalidLocalPackage")]
project.packages = ["invalidLocalPackage": .local(path: "invalidLocalPackage", group: nil)]
project.settingGroups = ["settingGroup1": Settings(
configSettings: ["invalidSettingGroupConfig": [:]],
groups: ["invalidSettingGroupSettingGroup"]
@@ -1227,9 +1227,10 @@ class SpecLoadingTests: XCTestCase {
"package6": .remote(url: "package.git", versionRequirement: .range(from: "1.2.0", to: "1.2.5")),
"package7": .remote(url: "package.git", versionRequirement: .exact("1.2.2")),
"package8": .remote(url: "package.git", versionRequirement: .upToNextMajorVersion("4.0.0-beta.5")),
"package9": .local(path: "package/package"),
"package9": .local(path: "package/package", group: nil),
"package10": .remote(url: "https://github.com/yonaskolb/XcodeGen", versionRequirement: .exact("1.2.2")),
"XcodeGen": .local(path: "../XcodeGen"),
"XcodeGen": .local(path: "../XcodeGen", group: nil),
"package11": .local(path: "../XcodeGen", group: "Packages/Feature"),
], options: .init(localPackagesGroup: "MyPackages"))
let dictionary: [String: Any] = [
@@ -1248,6 +1249,7 @@ class SpecLoadingTests: XCTestCase {
"package8": ["url": "package.git", "majorVersion": "4.0.0-beta.5"],
"package9": ["path": "package/package"],
"package10": ["github": "yonaskolb/XcodeGen", "exactVersion": "1.2.2"],
"package11": ["path": "../XcodeGen", "group": "Packages/Feature"],
],
"localPackages": ["../XcodeGen"],
]
@@ -1257,8 +1259,8 @@ class SpecLoadingTests: XCTestCase {
$0.it("parses old local package format") {
let project = Project(name: "spm", packages: [
"XcodeGen": .local(path: "../XcodeGen"),
"Yams": .local(path: "Yams"),
"XcodeGen": .local(path: "../XcodeGen", group: nil),
"Yams": .local(path: "Yams", group: nil),
], options: .init(localPackagesGroup: "MyPackages"))
let dictionary: [String: Any] = [
@@ -1271,7 +1271,7 @@ class ProjectGeneratorTests: XCTestCase {
let project = Project(name: "test", targets: [app], packages: [
"XcodeGen": .remote(url: "http://github.com/yonaskolb/XcodeGen", versionRequirement: .branch("master")),
"Codability": .remote(url: "http://github.com/yonaskolb/Codability", versionRequirement: .exact("1.0.0")),
"Yams": .local(path: "../Yams"),
"Yams": .local(path: "../Yams", group: nil),
], options: .init(localPackagesGroup: "MyPackages"))
let pbxProject = try project.generatePbxProj(specValidate: false)
@@ -1304,7 +1304,38 @@ class ProjectGeneratorTests: XCTestCase {
]
)
let project = Project(name: "test", targets: [app], packages: ["XcodeGen": .local(path: "../XcodeGen")])
let project = Project(name: "test", targets: [app], packages: ["XcodeGen": .local(path: "../XcodeGen", group: nil)])
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 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 packages with custom xcode path") {
let app = Target(
name: "MyApp",
type: .application,
platform: .iOS,
dependencies: [
Dependency(type: .package(product: nil), reference: "XcodeGen"),
]
)
let project = Project(name: "test", targets: [app], packages: ["XcodeGen": .local(path: "../XcodeGen", group: "Packages/Feature")])
let pbxProject = try project.generatePbxProj(specValidate: false)
let nativeTarget = try unwrap(pbxProject.nativeTargets.first(where: { $0.name == app.name }))
@@ -404,7 +404,7 @@ class SchemeGeneratorTests: XCTestCase {
name: "test",
targets: [framework],
schemes: [scheme],
packages: ["XcodeGen": .local(path: "../")],
packages: ["XcodeGen": .local(path: "../", group: nil)],
projectReferences: [
ProjectReference(name: "TestProject", path: externalProject.string),
]
@@ -493,7 +493,7 @@ class SchemeGeneratorTests: XCTestCase {
let project = Project(
name: "ios_test",
targets: [app],
packages: ["XcodeGen": .local(path: "../")]
packages: ["XcodeGen": .local(path: "../", group: nil)]
)
let xcodeProject = try project.generateXcodeProject()
let xcscheme = try unwrap(xcodeProject.sharedData?.schemes.first)