diff --git a/Sources/XcodeGenKit/PBXProjGenerator.swift b/Sources/XcodeGenKit/PBXProjGenerator.swift index 604ff473..02dbb35a 100644 --- a/Sources/XcodeGenKit/PBXProjGenerator.swift +++ b/Sources/XcodeGenKit/PBXProjGenerator.swift @@ -1491,6 +1491,13 @@ public class PBXProjGenerator { var exceptions: Set = Set( sourceGenerator.syncedFolderExceptions(for: targetSource, at: syncedPath) .compactMap { try? $0.relativePath(from: syncedPath).string } + .map { rel in + // Xcode only honors localized exclusions in the variant-group form; per-language paths are ignored. + var parts = rel.split(separator: "/") + guard let i = parts.firstIndex(where: { $0.hasSuffix(".lproj") }) else { return rel } + parts.remove(at: i) + return "/Localized: \(parts.joined(separator: "/"))" + } ) for infoPlistPath in Set(infoPlistFiles.values) { diff --git a/Tests/XcodeGenKitTests/SourceGeneratorTests.swift b/Tests/XcodeGenKitTests/SourceGeneratorTests.swift index b8450c5f..2f86f79b 100644 --- a/Tests/XcodeGenKitTests/SourceGeneratorTests.swift +++ b/Tests/XcodeGenKitTests/SourceGeneratorTests.swift @@ -203,6 +203,39 @@ class SourceGeneratorTests: XCTestCase { try expect(exceptions.contains("a.swift")) == false } + $0.it("excludes localized files from synced folders using the variant-group form") { + let directories = """ + Sources: + - a.swift + - b.swift + - Resources: + - en.lproj: + - Localizable.strings + - AppShortcuts.strings + - fr.lproj: + - Localizable.strings + - AppShortcuts.strings + """ + try createDirectories(directories) + + let source = TargetSource(path: "Sources", includes: ["a.swift", "Resources/*.lproj/Localizable.strings"], type: .syncedFolder) + let target = Target(name: "Test", type: .application, platform: .iOS, sources: [source]) + 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) + + let exceptionSet = try unwrap(syncedFolder.exceptions?.first as? PBXFileSystemSynchronizedBuildFileExceptionSet) + let exceptions = try unwrap(exceptionSet.membershipExceptions) + + try expect(exceptions.contains("/Localized: Resources/AppShortcuts.strings")) == true + try expect(exceptions.contains("Resources/en.lproj/AppShortcuts.strings")) == false + try expect(exceptions.contains("Resources/fr.lproj/AppShortcuts.strings")) == false + try expect(exceptions.contains("/Localized: Resources/Localizable.strings")) == false + try expect(exceptions.contains("b.swift")) == true + } + $0.it("adds membership exceptions for nested synced folder with intermediate groups") { let directories = """ Sources: