diff --git a/.gitignore b/.gitignore index cf9a40a5..cb21d3e8 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ XcodeGen.xcodeproj xcodegen.zip xcodegen.artifactbundle.zip .vscode/launch.json +DerivedData diff --git a/Docs/ProjectSpec.md b/Docs/ProjectSpec.md index 5dfec8f3..6780e13d 100644 --- a/Docs/ProjectSpec.md +++ b/Docs/ProjectSpec.md @@ -1246,6 +1246,7 @@ Swift packages are defined at a project level, and then linked to individual tar - [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. Use `""` to specify the project root. +- [ ] **excludeFromProject** : **String**- Optional flag to exclude the package from the generated project (useful if the package is already added via xcworkspace and the project is not intended for standalone use), defaults to `false` ```yml packages: @@ -1260,6 +1261,7 @@ packages: AppFeature: path: ../Packages group: Domains/AppFeature + excludeFromProject: false ``` ## Project Reference diff --git a/Sources/ProjectSpec/Project.swift b/Sources/ProjectSpec/Project.swift index 201cd0f8..527a7b9a 100644 --- a/Sources/ProjectSpec/Project.swift +++ b/Sources/ProjectSpec/Project.swift @@ -202,7 +202,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, group: nil) + $0[packageName] = .local(path: $1, group: nil, excludeFromProject: false) } ) } diff --git a/Sources/ProjectSpec/SpecValidation.swift b/Sources/ProjectSpec/SpecValidation.swift index 3cb9d8aa..4aa9c792 100644 --- a/Sources/ProjectSpec/SpecValidation.swift +++ b/Sources/ProjectSpec/SpecValidation.swift @@ -57,7 +57,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)) } } diff --git a/Sources/ProjectSpec/SwiftPackage.swift b/Sources/ProjectSpec/SwiftPackage.swift index e82ea2c0..0e752c5f 100644 --- a/Sources/ProjectSpec/SwiftPackage.swift +++ b/Sources/ProjectSpec/SwiftPackage.swift @@ -10,7 +10,7 @@ public enum SwiftPackage: Equatable { static let githubPrefix = "https://github.com/" case remote(url: String, versionRequirement: VersionRequirement) - case local(path: String, group: String?) + case local(path: String, group: String?, excludeFromProject: Bool) public var isLocal: Bool { if case .local = self { @@ -23,10 +23,10 @@ public enum SwiftPackage: Equatable { extension SwiftPackage: JSONObjectConvertible { public init(jsonDictionary: JSONDictionary) throws { - 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) + if let path: String = jsonDictionary.json(atKeyPath: "path") { + let customLocation: String? = jsonDictionary.json(atKeyPath: "group") + let excludeFromProject: Bool = jsonDictionary.json(atKeyPath: "excludeFromProject") ?? false + self = .local(path: path, group: customLocation, excludeFromProject: excludeFromProject) } else { let versionRequirement: VersionRequirement = try VersionRequirement(jsonDictionary: jsonDictionary) try Self.validateVersion(versionRequirement: versionRequirement) @@ -92,9 +92,10 @@ extension SwiftPackage: JSONEncodable { dictionary["revision"] = revision } return dictionary - case let .local(path, group): + case let .local(path, group, excludeFromProject): dictionary["path"] = path dictionary["group"] = group + dictionary["excludeFromProject"] = excludeFromProject } return dictionary diff --git a/Sources/XcodeGenKit/PBXProjGenerator.swift b/Sources/XcodeGenKit/PBXProjGenerator.swift index a5f667af..f4d486a0 100644 --- a/Sources/XcodeGenKit/PBXProjGenerator.swift +++ b/Sources/XcodeGenKit/PBXProjGenerator.swift @@ -169,12 +169,14 @@ public class PBXProjGenerator { let packageReference = XCRemoteSwiftPackageReference(repositoryURL: url, versionRequirement: versionRequirement) packageReferences[name] = packageReference addObject(packageReference) - case let .local(path, group): + case let .local(path, group, excludeFromProject): let packageReference = XCLocalSwiftPackageReference(relativePath: path) localPackageReferences[name] = packageReference - addObject(packageReference) - - try sourceGenerator.createLocalPackage(path: Path(path), group: group.map { Path($0) }) + + if !excludeFromProject { + addObject(packageReference) + try sourceGenerator.createLocalPackage(path: Path(path), group: group.map { Path($0) }) + } } } diff --git a/Sources/XcodeGenKit/SchemeGenerator.swift b/Sources/XcodeGenKit/SchemeGenerator.swift index ac100e89..90ef213a 100644 --- a/Sources/XcodeGenKit/SchemeGenerator.swift +++ b/Sources/XcodeGenKit/SchemeGenerator.swift @@ -173,7 +173,7 @@ public class SchemeGenerator { switch target.location { case .package(let packageName): guard let package = self.project.getPackage(packageName), - case let .local(path, _) = package else { + case let .local(path, _, _) = package else { throw SchemeGenerationError.missingPackage(packageName) } return XCScheme.BuildableReference( diff --git a/Tests/ProjectSpecTests/ProjectSpecTests.swift b/Tests/ProjectSpecTests/ProjectSpecTests.swift index 08a9f0c3..311597ad 100644 --- a/Tests/ProjectSpecTests/ProjectSpecTests.swift +++ b/Tests/ProjectSpecTests/ProjectSpecTests.swift @@ -137,7 +137,7 @@ class ProjectSpecTests: XCTestCase { project.settings = invalidSettings project.configFiles = ["invalidConfig": "invalidConfigFile"] project.fileGroups = ["invalidFileGroup"] - project.packages = ["invalidLocalPackage": .local(path: "invalidLocalPackage", group: nil)] + project.packages = ["invalidLocalPackage": .local(path: "invalidLocalPackage", group: nil, excludeFromProject: false)] project.settingGroups = ["settingGroup1": Settings( configSettings: ["invalidSettingGroupConfig": [:]], groups: ["invalidSettingGroupSettingGroup"] diff --git a/Tests/ProjectSpecTests/SpecLoadingTests.swift b/Tests/ProjectSpecTests/SpecLoadingTests.swift index 055088f9..7a00f65e 100644 --- a/Tests/ProjectSpecTests/SpecLoadingTests.swift +++ b/Tests/ProjectSpecTests/SpecLoadingTests.swift @@ -1489,10 +1489,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", group: nil), + "package9": .local(path: "package/package", group: nil, excludeFromProject: false), "package10": .remote(url: "https://github.com/yonaskolb/XcodeGen", versionRequirement: .exact("1.2.2")), - "XcodeGen": .local(path: "../XcodeGen", group: nil), - "package11": .local(path: "../XcodeGen", group: "Packages/Feature"), + "XcodeGen": .local(path: "../XcodeGen", group: nil, excludeFromProject: false), + "package11": .local(path: "../XcodeGen", group: "Packages/Feature", excludeFromProject: false), ], options: .init(localPackagesGroup: "MyPackages")) let dictionary: [String: Any] = [ @@ -1521,8 +1521,8 @@ class SpecLoadingTests: XCTestCase { $0.it("parses old local package format") { let project = Project(name: "spm", packages: [ - "XcodeGen": .local(path: "../XcodeGen", group: nil), - "Yams": .local(path: "Yams", group: nil), + "XcodeGen": .local(path: "../XcodeGen", group: nil, excludeFromProject: false), + "Yams": .local(path: "Yams", group: nil, excludeFromProject: false), ], options: .init(localPackagesGroup: "MyPackages")) let dictionary: [String: Any] = [ diff --git a/Tests/XcodeGenKitTests/PBXProjGeneratorTests.swift b/Tests/XcodeGenKitTests/PBXProjGeneratorTests.swift index b067fa56..31873cdc 100644 --- a/Tests/XcodeGenKitTests/PBXProjGeneratorTests.swift +++ b/Tests/XcodeGenKitTests/PBXProjGeneratorTests.swift @@ -325,9 +325,9 @@ class PBXProjGeneratorTests: XCTestCase { name: "Test", targets: [target], packages: [ - "Common": .local(path: "Packages/Common", group: nil), - "FeatureA": .local(path: "Packages/FeatureA", group: nil), - "FeatureB": .local(path: "Packages/FeatureB", group: nil), + "Common": .local(path: "Packages/Common", group: nil, excludeFromProject: false), + "FeatureA": .local(path: "Packages/FeatureA", group: nil, excludeFromProject: false), + "FeatureB": .local(path: "Packages/FeatureB", group: nil, excludeFromProject: false), ], options: options ) diff --git a/Tests/XcodeGenKitTests/ProjectGeneratorTests.swift b/Tests/XcodeGenKitTests/ProjectGeneratorTests.swift index 194dc082..64f9f5e4 100644 --- a/Tests/XcodeGenKitTests/ProjectGeneratorTests.swift +++ b/Tests/XcodeGenKitTests/ProjectGeneratorTests.swift @@ -1548,7 +1548,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", group: nil), + "Yams": .local(path: "../Yams", group: nil, excludeFromProject: false), ], options: .init(localPackagesGroup: "MyPackages")) let pbxProject = try project.generatePbxProj(specValidate: false) @@ -1581,7 +1581,7 @@ class ProjectGeneratorTests: XCTestCase { ] ) - let project = Project(name: "test", targets: [app], packages: ["XcodeGen": .local(path: "../XcodeGen", group: nil)]) + let project = Project(name: "test", targets: [app], packages: ["XcodeGen": .local(path: "../XcodeGen", group: nil, excludeFromProject: false)]) let pbxProject = try project.generatePbxProj(specValidate: false) let nativeTarget = try unwrap(pbxProject.nativeTargets.first(where: { $0.name == app.name })) @@ -1604,7 +1604,49 @@ class ProjectGeneratorTests: XCTestCase { try expect(file.product?.productName) == "XcodeGen" } - + + $0.it("excludes local swift packages from generated project if needed") { + let app = Target( + name: "MyApp", + type: .application, + platform: .iOS, + dependencies: [ + Dependency(type: .package(products: ["XcodeGen"]), reference: "XcodeGen"), + ] + ) + + let project = Project( + name: "test", + targets: [app], + packages: [ + "XcodeGen": .local( + path: "../XcodeGen", + group: nil, + excludeFromProject: true + ) + ] + ) + + let pbxProject = try project.generatePbxProj(specValidate: false) + let nativeTarget = try unwrap(pbxProject.nativeTargets.first(where: { $0.name == app.name })) + let localPackageFile = pbxProject.fileReferences.first(where: { $0.path == "../XcodeGen" }) + + try expect(localPackageFile).to.beNil() + try expect(pbxProject.rootObject?.localPackages.count) == 0 + + 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", @@ -1616,7 +1658,7 @@ class ProjectGeneratorTests: XCTestCase { ) let customLocalPackageGroup = "Packages/Feature" - let project = Project(name: "test", targets: [app], packages: ["XcodeGen": .local(path: "../XcodeGen", group: customLocalPackageGroup)]) + let project = Project(name: "test", targets: [app], packages: ["XcodeGen": .local(path: "../XcodeGen", group: customLocalPackageGroup, excludeFromProject: false)]) let pbxProject = try project.generatePbxProj(specValidate: false) let nativeTarget = try unwrap(pbxProject.nativeTargets.first(where: { $0.name == app.name })) @@ -1657,7 +1699,7 @@ class ProjectGeneratorTests: XCTestCase { ] ) - let project = Project(name: "test", targets: [app], packages: ["XcodeGen": .local(path: "../XcodeGen", group: "")]) + let project = Project(name: "test", targets: [app], packages: ["XcodeGen": .local(path: "../XcodeGen", group: "", excludeFromProject: false)]) let pbxProject = try project.generatePbxProj(specValidate: false) let nativeTarget = try unwrap(pbxProject.nativeTargets.first(where: { $0.name == app.name })) @@ -1691,7 +1733,7 @@ class ProjectGeneratorTests: XCTestCase { ] ) - let project = Project(name: "test", targets: [app], packages: ["XcodeGen": .local(path: "../XcodeGen", group: nil)], options: .init(localPackagesGroup: "")) + let project = Project(name: "test", targets: [app], packages: ["XcodeGen": .local(path: "../XcodeGen", group: nil, excludeFromProject: false)], options: .init(localPackagesGroup: "")) let pbxProject = try project.generatePbxProj(specValidate: false) let nativeTarget = try unwrap(pbxProject.nativeTargets.first(where: { $0.name == app.name })) @@ -1867,7 +1909,7 @@ class ProjectGeneratorTests: XCTestCase { ) let project = Project(name: "test", targets: [app], packages: [ - "FooFeature": .local(path: "../FooFeature", group: nil) + "FooFeature": .local(path: "../FooFeature", group: nil, excludeFromProject: false) ], options: .init(localPackagesGroup: "MyPackages")) let pbxProject = try project.generatePbxProj(specValidate: false) diff --git a/Tests/XcodeGenKitTests/SchemeGeneratorTests.swift b/Tests/XcodeGenKitTests/SchemeGeneratorTests.swift index 3aad22c5..d7527200 100644 --- a/Tests/XcodeGenKitTests/SchemeGeneratorTests.swift +++ b/Tests/XcodeGenKitTests/SchemeGeneratorTests.swift @@ -406,7 +406,7 @@ class SchemeGeneratorTests: XCTestCase { name: "test", targets: [framework], schemes: [scheme], - packages: ["XcodeGen": .local(path: "../", group: nil)], + packages: ["XcodeGen": .local(path: "../", group: nil, excludeFromProject: false)], projectReferences: [ ProjectReference(name: "TestProject", path: externalProject.string), ] @@ -570,7 +570,7 @@ class SchemeGeneratorTests: XCTestCase { let project = Project( name: "ios_test", targets: [app], - packages: ["XcodeGen": .local(path: "../", group: nil)] + packages: ["XcodeGen": .local(path: "../", group: nil, excludeFromProject: false)] ) let xcodeProject = try project.generateXcodeProject() let xcscheme = try unwrap(xcodeProject.sharedData?.schemes.first)