From 9363be723c5526848e36b0e7838bf57405380769 Mon Sep 17 00:00:00 2001 From: Yonas Kolb Date: Sun, 6 Apr 2025 20:42:19 +1000 Subject: [PATCH 1/3] update xcodeproj to 8.27.7 --- Package.resolved | 8 ++++---- Package.swift | 2 +- Sources/XcodeGenKit/Version.swift | 2 +- .../CarthageProject/Project.xcodeproj/project.pbxproj | 6 ++---- Tests/Fixtures/SPM/SPM.xcodeproj/project.pbxproj | 6 ++---- .../SPM/SPM.xcodeproj/xcshareddata/xcschemes/App.xcscheme | 6 ++++-- .../AnotherProject.xcodeproj/project.pbxproj | 6 ++---- .../TestProject/Project.xcodeproj/project.pbxproj | 4 ++-- .../xcshareddata/xcschemes/App_Clip.xcscheme | 6 ++++-- .../xcshareddata/xcschemes/App_Scheme.xcscheme | 4 ++-- .../xcshareddata/xcschemes/App_iOS Production.xcscheme | 6 ++++-- .../xcshareddata/xcschemes/App_iOS Staging.xcscheme | 6 ++++-- .../xcshareddata/xcschemes/App_iOS Test.xcscheme | 6 ++++-- .../scheme_test/TestProject.xcodeproj/project.pbxproj | 6 ++---- 14 files changed, 38 insertions(+), 36 deletions(-) diff --git a/Package.resolved b/Package.resolved index 237f873fa..671959c8b 100644 --- a/Package.resolved +++ b/Package.resolved @@ -5,8 +5,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/tadija/AEXML.git", "state" : { - "revision" : "38f7d00b23ecd891e1ee656fa6aeebd6ba04ecc3", - "version" : "4.6.1" + "revision" : "db806756c989760b35108146381535aec231092b", + "version" : "4.7.0" } }, { @@ -77,8 +77,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/tuist/XcodeProj.git", "state" : { - "revision" : "dc3b87a4e69f9cd06c6cb16199f5d0472e57ef6b", - "version" : "8.24.3" + "revision" : "b1caa062d4aaab3e3d2bed5fe0ac5f8ce9bf84f4", + "version" : "8.27.7" } }, { diff --git a/Package.swift b/Package.swift index 99c7649c0..ab86ad008 100644 --- a/Package.swift +++ b/Package.swift @@ -16,7 +16,7 @@ let package = Package( .package(url: "https://github.com/yonaskolb/JSONUtilities.git", from: "4.2.0"), .package(url: "https://github.com/kylef/Spectre.git", from: "0.9.2"), .package(url: "https://github.com/onevcat/Rainbow.git", from: "4.0.0"), - .package(url: "https://github.com/tuist/XcodeProj.git", exact: "8.24.3"), + .package(url: "https://github.com/tuist/XcodeProj.git", exact: "8.27.7"), .package(url: "https://github.com/jakeheis/SwiftCLI.git", from: "6.0.3"), .package(url: "https://github.com/mxcl/Version", from: "2.0.0"), .package(url: "https://github.com/freddi-kit/ArtifactBundleGen", exact: "0.0.6") diff --git a/Sources/XcodeGenKit/Version.swift b/Sources/XcodeGenKit/Version.swift index 57618046d..007d26f64 100644 --- a/Sources/XcodeGenKit/Version.swift +++ b/Sources/XcodeGenKit/Version.swift @@ -16,7 +16,7 @@ extension Project { } var objectVersion: UInt { - 54 + 70 } var minimizedProjectReferenceProxies: Int { diff --git a/Tests/Fixtures/CarthageProject/Project.xcodeproj/project.pbxproj b/Tests/Fixtures/CarthageProject/Project.xcodeproj/project.pbxproj index eb6cf67fe..3289e3bdd 100644 --- a/Tests/Fixtures/CarthageProject/Project.xcodeproj/project.pbxproj +++ b/Tests/Fixtures/CarthageProject/Project.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 54; + objectVersion = 70; objects = { /* Begin PBXBuildFile section */ @@ -322,8 +322,6 @@ attributes = { BuildIndependentTargetsInParallel = YES; LastUpgradeCheck = 1430; - TargetAttributes = { - }; }; buildConfigurationList = D91E14E36EC0B415578456F2 /* Build configuration list for PBXProject "Project" */; compatibilityVersion = "Xcode 14.0"; @@ -335,7 +333,7 @@ ); mainGroup = 293D0FF827366B513839236A; minimizedProjectReferenceProxies = 1; - preferredProjectObjectVersion = 54; + preferredProjectObjectVersion = 70; projectDirPath = ""; projectRoot = ""; targets = ( diff --git a/Tests/Fixtures/SPM/SPM.xcodeproj/project.pbxproj b/Tests/Fixtures/SPM/SPM.xcodeproj/project.pbxproj index 59311f725..7918465d3 100644 --- a/Tests/Fixtures/SPM/SPM.xcodeproj/project.pbxproj +++ b/Tests/Fixtures/SPM/SPM.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 54; + objectVersion = 70; objects = { /* Begin PBXAggregateTarget section */ @@ -239,8 +239,6 @@ attributes = { BuildIndependentTargetsInParallel = YES; LastUpgradeCheck = 1430; - TargetAttributes = { - }; }; buildConfigurationList = 425866ADA259DB93FC4AF1E3 /* Build configuration list for PBXProject "SPM" */; compatibilityVersion = "Xcode 14.0"; @@ -259,7 +257,7 @@ 630A8CE9F2BE39704ED9D461 /* XCLocalSwiftPackageReference "FooFeature" */, C6539B364583AE96C18CE377 /* XCLocalSwiftPackageReference "../../.." */, ); - preferredProjectObjectVersion = 54; + preferredProjectObjectVersion = 70; projectDirPath = ""; projectRoot = ""; targets = ( diff --git a/Tests/Fixtures/SPM/SPM.xcodeproj/xcshareddata/xcschemes/App.xcscheme b/Tests/Fixtures/SPM/SPM.xcodeproj/xcshareddata/xcschemes/App.xcscheme index 353d320e0..298045bf8 100644 --- a/Tests/Fixtures/SPM/SPM.xcodeproj/xcshareddata/xcschemes/App.xcscheme +++ b/Tests/Fixtures/SPM/SPM.xcodeproj/xcshareddata/xcschemes/App.xcscheme @@ -40,7 +40,8 @@ + skipped = "NO" + parallelizable = "NO"> + skipped = "NO" + parallelizable = "NO"> + skipped = "NO" + parallelizable = "NO"> + skipped = "NO" + parallelizable = "NO"> + skipped = "NO" + parallelizable = "NO"> + skipped = "NO" + parallelizable = "NO"> + skipped = "NO" + parallelizable = "NO"> + skipped = "NO" + parallelizable = "NO"> + skipped = "NO" + parallelizable = "NO"> + skipped = "NO" + parallelizable = "NO"> + skipped = "NO" + parallelizable = "NO"> Date: Tue, 15 Apr 2025 22:14:11 +1000 Subject: [PATCH 2/3] add syncedFolder source type --- Docs/ProjectSpec.md | 5 +++ Sources/ProjectSpec/SourceType.swift | 1 + Sources/ProjectSpec/SpecOptions.swift | 6 ++- Sources/XcodeGenKit/PBXProjGenerator.swift | 6 +++ Sources/XcodeGenKit/SourceGenerator.swift | 40 ++++++++++++++++++- Sources/XcodeGenKit/XCProjExtensions.swift | 2 + .../TestProject/App_iOS/AppDelegate.swift | 8 +++- .../Project.xcodeproj/project.pbxproj | 16 ++++++++ .../TestProject/SyncedFolder/SyncedFile.swift | 4 ++ Tests/Fixtures/TestProject/project.yml | 2 + .../SourceGeneratorTests.swift | 36 +++++++++++++++++ 11 files changed, 123 insertions(+), 3 deletions(-) create mode 100644 Tests/Fixtures/TestProject/SyncedFolder/SyncedFile.swift diff --git a/Docs/ProjectSpec.md b/Docs/ProjectSpec.md index 83166e9c4..5a4aa0dde 100644 --- a/Docs/ProjectSpec.md +++ b/Docs/ProjectSpec.md @@ -153,6 +153,10 @@ Note that target names can also be changed by adding a `name` property to a targ - [ ] **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. - [ ] **useBaseInternationalization**: **Bool** If this is `false` and your project does not include resources located in a **Base.lproj** directory then `Base` will not be included in the projects 'known regions'. The default value is `true`. - [ ] **schemePathPrefix**: **String** - A path prefix for relative paths in schemes, such as StoreKitConfiguration. The default is `"../../"`, which is suitable for non-workspace projects. For use in workspaces, use `"../"`. +- [ ] **defaultSourceDirectoryType**: **String** - When a [Target source](#target-source) doesn't specify a type and is a directory, this is the type that will be used. If nothing is specified for either then `group` will be used. + - `group` (default) + - `folder` + - `syncedFolder` ```yaml options: @@ -542,6 +546,7 @@ A source can be provided via a string (the path) or an object of the form: - `file`: a file reference with a parent group will be created (Default for files or directories with extensions) - `group`: a group with all it's containing files. (Default for directories without extensions) - `folder`: a folder reference. + - `syncedFolder`: Xcode 16's synchronized folders, also knows as buildable folders - [ ] **headerVisibility**: **String** - The visibility of any headers. This defaults to `public`, but can be either: - `public` - `private` diff --git a/Sources/ProjectSpec/SourceType.swift b/Sources/ProjectSpec/SourceType.swift index 77ce4ffe7..9fe009321 100644 --- a/Sources/ProjectSpec/SourceType.swift +++ b/Sources/ProjectSpec/SourceType.swift @@ -11,4 +11,5 @@ public enum SourceType: String { case group case file case folder + case syncedFolder } diff --git a/Sources/ProjectSpec/SpecOptions.swift b/Sources/ProjectSpec/SpecOptions.swift index a5df4e28d..c10c34698 100644 --- a/Sources/ProjectSpec/SpecOptions.swift +++ b/Sources/ProjectSpec/SpecOptions.swift @@ -37,6 +37,7 @@ public struct SpecOptions: Equatable { public var postGenCommand: String? public var useBaseInternationalization: Bool public var schemePathPrefix: String + public var defaultSourceDirectoryType: SourceType? public enum ValidationType: String { case missingConfigs @@ -100,7 +101,8 @@ public struct SpecOptions: Equatable { preGenCommand: String? = nil, postGenCommand: String? = nil, useBaseInternationalization: Bool = useBaseInternationalizationDefault, - schemePathPrefix: String = schemePathPrefixDefault + schemePathPrefix: String = schemePathPrefixDefault, + defaultSourceDirectoryType: SourceType? = nil ) { self.minimumXcodeGenVersion = minimumXcodeGenVersion self.carthageBuildPath = carthageBuildPath @@ -127,6 +129,7 @@ public struct SpecOptions: Equatable { self.postGenCommand = postGenCommand self.useBaseInternationalization = useBaseInternationalization self.schemePathPrefix = schemePathPrefix + self.defaultSourceDirectoryType = defaultSourceDirectoryType } } @@ -160,6 +163,7 @@ extension SpecOptions: JSONObjectConvertible { postGenCommand = jsonDictionary.json(atKeyPath: "postGenCommand") useBaseInternationalization = jsonDictionary.json(atKeyPath: "useBaseInternationalization") ?? SpecOptions.useBaseInternationalizationDefault schemePathPrefix = jsonDictionary.json(atKeyPath: "schemePathPrefix") ?? SpecOptions.schemePathPrefixDefault + defaultSourceDirectoryType = jsonDictionary.json(atKeyPath: "defaultSourceDirectoryType") if jsonDictionary["fileTypes"] != nil { fileTypes = try jsonDictionary.json(atKeyPath: "fileTypes") } else { diff --git a/Sources/XcodeGenKit/PBXProjGenerator.swift b/Sources/XcodeGenKit/PBXProjGenerator.swift index c7f5f2d1f..cd683091a 100644 --- a/Sources/XcodeGenKit/PBXProjGenerator.swift +++ b/Sources/XcodeGenKit/PBXProjGenerator.swift @@ -1454,6 +1454,12 @@ public class PBXProjGenerator { if !target.isLegacy { targetObject.productType = target.type } + + // add fileSystemSynchronizedGroups + let synchronizedRootGroups = sourceFiles.compactMap { $0.synchronizedRootGroup } + if !synchronizedRootGroups.isEmpty { + targetObject.fileSystemSynchronizedGroups = synchronizedRootGroups + } } private func makePlatformFilter(for filter: Dependency.PlatformFilter) -> String? { diff --git a/Sources/XcodeGenKit/SourceGenerator.swift b/Sources/XcodeGenKit/SourceGenerator.swift index 0b8f93b70..4da2e0608 100644 --- a/Sources/XcodeGenKit/SourceGenerator.swift +++ b/Sources/XcodeGenKit/SourceGenerator.swift @@ -9,6 +9,7 @@ struct SourceFile { let fileReference: PBXFileElement let buildFile: PBXBuildFile let buildPhase: BuildPhaseSpec? + var synchronizedRootGroup: PBXFileSystemSynchronizedRootGroup? } class SourceGenerator { @@ -687,6 +688,33 @@ class SourceGenerator { sourceFiles += groupSourceFiles sourceReference = group + case .syncedFolder: + + let relativePath = (try? path.relativePath(from: project.basePath)) ?? path + + let syncedRootGroup = PBXFileSystemSynchronizedRootGroup( + sourceTree: .group, + path: relativePath.string, + name: targetSource.name, + explicitFileTypes: [:], + exceptions: [], + explicitFolders: [] + ) + addObject(syncedRootGroup) + sourceReference = syncedRootGroup + + // TODO: adjust if hasCustomParent == true + rootGroups.insert(syncedRootGroup) + + var sourceFile = generateSourceFile( + targetType: targetType, + targetSource: targetSource, + path: path, + fileReference: syncedRootGroup, + buildPhases: buildPhases + ) + sourceFile.synchronizedRootGroup = syncedRootGroup + sourceFiles.append(sourceFile) } if hasCustomParent { @@ -703,7 +731,17 @@ class SourceGenerator { /// /// While `TargetSource` declares `type`, its optional and in the event that the value is not defined then we must resolve a sensible default based on the path of the source. private func resolvedTargetSourceType(for targetSource: TargetSource, at path: Path) -> SourceType { - return targetSource.type ?? (path.isFile || path.extension != nil ? .file : .group) + if let chosenType = targetSource.type { + return chosenType + } else { + if path.isFile || path.extension != nil { + return .file + } else if let sourceType = project.options.defaultSourceDirectoryType { + return sourceType + } else { + return .group + } + } } private func createParentGroups(_ parentGroups: [String], for fileElement: PBXFileElement) { diff --git a/Sources/XcodeGenKit/XCProjExtensions.swift b/Sources/XcodeGenKit/XCProjExtensions.swift index ec3f2763b..33d3f5b67 100644 --- a/Sources/XcodeGenKit/XCProjExtensions.swift +++ b/Sources/XcodeGenKit/XCProjExtensions.swift @@ -38,6 +38,8 @@ extension PBXProj { string += "\n 🌎 " + variantGroup.nameOrPath } else if let versionGroup = child as? XCVersionGroup { string += "\n 🔢 " + versionGroup.nameOrPath + } else if let syncedFolder = child as? PBXFileSystemSynchronizedRootGroup { + string += "\n 📁 " + syncedFolder.nameOrPath } } return string diff --git a/Tests/Fixtures/TestProject/App_iOS/AppDelegate.swift b/Tests/Fixtures/TestProject/App_iOS/AppDelegate.swift index 2c79185ee..10c98bab3 100644 --- a/Tests/Fixtures/TestProject/App_iOS/AppDelegate.swift +++ b/Tests/Fixtures/TestProject/App_iOS/AppDelegate.swift @@ -7,10 +7,16 @@ class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { - // Override point for customization after application launch. + + // file from a framework _ = FrameworkStruct() + // Standalone files added to project by path-to-file. _ = standaloneHello() + + // file in a synced folder + _ = SyncedStruct() + return true } } diff --git a/Tests/Fixtures/TestProject/Project.xcodeproj/project.pbxproj b/Tests/Fixtures/TestProject/Project.xcodeproj/project.pbxproj index 836db5e5e..ce031a8c8 100644 --- a/Tests/Fixtures/TestProject/Project.xcodeproj/project.pbxproj +++ b/Tests/Fixtures/TestProject/Project.xcodeproj/project.pbxproj @@ -830,6 +830,18 @@ FED40A89162E446494DDE7C7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; /* End PBXFileReference section */ +/* Begin PBXFileSystemSynchronizedRootGroup section */ + AE2AB2772F70DFFF402AA02B /* SyncedFolder */ = { + isa = PBXFileSystemSynchronizedRootGroup; + explicitFileTypes = { + }; + explicitFolders = ( + ); + path = SyncedFolder; + sourceTree = ""; + }; +/* End PBXFileSystemSynchronizedRootGroup section */ + /* Begin PBXFrameworksBuildPhase section */ 117840B4DBC04099F6779D00 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; @@ -1050,6 +1062,7 @@ 2E1E747C7BC434ADB80CC269 /* Headers */, 6B1603BA83AA0C7B94E45168 /* ResourceFolder */, 6BBE762F36D94AB6FFBFE834 /* SomeFile */, + AE2AB2772F70DFFF402AA02B /* SyncedFolder */, 79DC4A1E4D2E0D3A215179BC /* Bundles */, FC1515684236259C50A7747F /* Frameworks */, AC523591AC7BE9275003D2DB /* Products */, @@ -1686,6 +1699,9 @@ E8C078B0A2A2B0E1D35694D5 /* PBXTargetDependency */, 981D116D40DBA0407D0E0E94 /* PBXTargetDependency */, ); + fileSystemSynchronizedGroups = ( + AE2AB2772F70DFFF402AA02B /* SyncedFolder */, + ); name = App_iOS; packageProductDependencies = ( D7917D10F77DA9D69937D493 /* Swinject */, diff --git a/Tests/Fixtures/TestProject/SyncedFolder/SyncedFile.swift b/Tests/Fixtures/TestProject/SyncedFolder/SyncedFile.swift new file mode 100644 index 000000000..331644108 --- /dev/null +++ b/Tests/Fixtures/TestProject/SyncedFolder/SyncedFile.swift @@ -0,0 +1,4 @@ + +struct SyncedStruct { + +} diff --git a/Tests/Fixtures/TestProject/project.yml b/Tests/Fixtures/TestProject/project.yml index 69e850a90..801d4275a 100644 --- a/Tests/Fixtures/TestProject/project.yml +++ b/Tests/Fixtures/TestProject/project.yml @@ -164,6 +164,8 @@ targets: - tag1 - tag2 - String Catalogs/LocalizableStrings.xcstrings + - path: SyncedFolder + type: syncedFolder settings: INFOPLIST_FILE: App_iOS/Info.plist PRODUCT_BUNDLE_IDENTIFIER: com.project.app diff --git a/Tests/XcodeGenKitTests/SourceGeneratorTests.swift b/Tests/XcodeGenKitTests/SourceGeneratorTests.swift index bdf955cef..4e109ab2f 100644 --- a/Tests/XcodeGenKitTests/SourceGeneratorTests.swift +++ b/Tests/XcodeGenKitTests/SourceGeneratorTests.swift @@ -84,6 +84,42 @@ class SourceGeneratorTests: XCTestCase { try pbxProj.expectFile(paths: ["Sources", "A", "C2.0", "c.swift"], buildPhase: .sources) } + $0.it("generates synced folder") { + let directories = """ + Sources: + A: + - a.swift + """ + try createDirectories(directories) + + let target = Target(name: "Test", type: .application, platform: .iOS, sources: [.init(path: "Sources", type: .syncedFolder)]) + let project = Project(basePath: directoryPath, name: "Test", targets: [target]) + + let pbxProj = try project.generatePbxProj() + let syncedFolders = try pbxProj.getMainGroup().children.compactMap { $0 as? PBXFileSystemSynchronizedRootGroup } + let syncedFolder = try unwrap(syncedFolders.first) + + try expect([syncedFolder]) == pbxProj.nativeTargets.first?.fileSystemSynchronizedGroups + } + + $0.it("respects defaultSourceDirectoryType") { + let directories = """ + Sources: + A: + - a.swift + """ + try createDirectories(directories) + + let target = Target(name: "Test", type: .application, platform: .iOS, sources: ["Sources"]) + let project = Project(basePath: directoryPath, name: "Test", targets: [target], options: .init(defaultSourceDirectoryType: .syncedFolder)) + + let pbxProj = try project.generatePbxProj() + let syncedFolders = try pbxProj.getMainGroup().children.compactMap { $0 as? PBXFileSystemSynchronizedRootGroup } + let syncedFolder = try unwrap(syncedFolders.first) + + try expect([syncedFolder]) == pbxProj.nativeTargets.first?.fileSystemSynchronizedGroups + } + $0.it("supports frameworks in sources") { let directories = """ Sources: From 5519fb08dbaa99c1370d46873fc0e05932402daf Mon Sep 17 00:00:00 2001 From: Yonas Kolb Date: Tue, 15 Apr 2025 22:31:06 +1000 Subject: [PATCH 3/3] drop xcode 15 support --- .github/workflows/ci.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fda76be5f..dce7abef5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,10 +8,8 @@ jobs: name: Xcode ${{ matrix.xcode }} strategy: matrix: - xcode: ["15.4", "16.0"] + xcode: ["16.0"] include: - - xcode: "15.4" - macos: macos-15 - xcode: "16.0" macos: macos-15 env: