From 11651bcd236db66438b66f72451a99c510c1936e Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Wed, 14 Oct 2026 09:58:36 +0300 Subject: [PATCH] Normalize generated schemes to match Xcode's output XcodeGen (through XcodeProj) serializes schemes slightly differently to Xcode, so Xcode rewrites them as soon as it touches them (running tests, switching test plans, opening the scheme editor), leaving recurring local changes behind. Add a small Swift script, run from XcodeGen's postGenCommand, that rewrites the generated schemes the way Xcode saves them: it drops default-valued attributes and empty CommandLineArguments, orders TestPlanReference attributes, and moves TestPlans to the end of the TestAction, omitting the redundant empty Testables. Regenerated schemes are byte-identical to the copies saved by Xcode. The script can be removed once https://github.com/tuist/XcodeProj/pull/1189 ships in XcodeGen. --- .../xcschemes/AccessibilityTests.xcscheme | 26 ++++++------------ .../xcshareddata/xcschemes/ElementX.xcscheme | 23 +++++----------- .../xcschemes/IntegrationTests.xcscheme | 23 +++++----------- .../xcshareddata/xcschemes/NSE.xcscheme | 12 ++------- .../xcschemes/PreviewTests.xcscheme | 23 +++++----------- .../xcschemes/ShareExtension.xcscheme | 12 ++------- .../xcshareddata/xcschemes/UITests.xcscheme | 26 ++++++------------ .../xcshareddata/xcschemes/UnitTests.xcscheme | 23 +++++----------- Tools/XcodeGen/normalizeSchemes.swift | 27 +++++++++++++++++++ Tools/XcodeGen/postGenCommand.sh | 3 +++ 10 files changed, 78 insertions(+), 120 deletions(-) create mode 100644 Tools/XcodeGen/normalizeSchemes.swift diff --git a/ElementX.xcodeproj/xcshareddata/xcschemes/AccessibilityTests.xcscheme b/ElementX.xcodeproj/xcshareddata/xcschemes/AccessibilityTests.xcscheme index 2baaca726..6545757e6 100644 --- a/ElementX.xcodeproj/xcshareddata/xcschemes/AccessibilityTests.xcscheme +++ b/ElementX.xcodeproj/xcshareddata/xcschemes/AccessibilityTests.xcscheme @@ -4,8 +4,7 @@ version = "1.7"> + buildImplicitDependencies = "YES"> - - - - + shouldUseLaunchSchemeArgsEnv = "YES"> - - - - + + + + - - - - + buildImplicitDependencies = "YES"> - - - - - - - - + + + + - - - - diff --git a/ElementX.xcodeproj/xcshareddata/xcschemes/IntegrationTests.xcscheme b/ElementX.xcodeproj/xcshareddata/xcschemes/IntegrationTests.xcscheme index 16f0cd2ad..77e119b37 100644 --- a/ElementX.xcodeproj/xcshareddata/xcschemes/IntegrationTests.xcscheme +++ b/ElementX.xcodeproj/xcshareddata/xcschemes/IntegrationTests.xcscheme @@ -4,8 +4,7 @@ version = "1.7"> + buildImplicitDependencies = "YES"> - - - - - - - - + + + + - - - - + buildImplicitDependencies = "YES"> + shouldUseLaunchSchemeArgsEnv = "YES"> - - - - - - diff --git a/ElementX.xcodeproj/xcshareddata/xcschemes/PreviewTests.xcscheme b/ElementX.xcodeproj/xcshareddata/xcschemes/PreviewTests.xcscheme index 9cbcccbb2..4bfd8d512 100644 --- a/ElementX.xcodeproj/xcshareddata/xcschemes/PreviewTests.xcscheme +++ b/ElementX.xcodeproj/xcshareddata/xcschemes/PreviewTests.xcscheme @@ -4,8 +4,7 @@ version = "1.7"> + buildImplicitDependencies = "YES"> - - - - - - - - + + + + - - - - + buildImplicitDependencies = "YES"> + shouldUseLaunchSchemeArgsEnv = "YES"> - - - - - - diff --git a/ElementX.xcodeproj/xcshareddata/xcschemes/UITests.xcscheme b/ElementX.xcodeproj/xcshareddata/xcschemes/UITests.xcscheme index cdf00b73b..40e9528bc 100644 --- a/ElementX.xcodeproj/xcshareddata/xcschemes/UITests.xcscheme +++ b/ElementX.xcodeproj/xcshareddata/xcschemes/UITests.xcscheme @@ -4,8 +4,7 @@ version = "1.7"> + buildImplicitDependencies = "YES"> - - - - + shouldUseLaunchSchemeArgsEnv = "YES"> - - - - + + + + - - - - + buildImplicitDependencies = "YES"> - - - - - - - - + + + + - - - - /#, with: "\">") + scheme.replace(#/\s*\s*<\/CommandLineArguments>/#, with: "") + scheme.replace(#/default = (?"[^"]*")(?\s*)reference = (?"[^"]*")/#) { match in + "reference = \(match.reference)\(match.indent)default = \(match.value)" + } + + // Xcode writes test plans last and omits the then redundant empty testables. + if let testPlans = scheme.firstMatch(of: #/\s*.*?<\/TestPlans>/#.dotMatchesNewlines()) { + scheme.removeSubrange(testPlans.range) + scheme.replace(#/\s*\s*<\/Testables>/#, with: "") + scheme.replace(#/\s*<\/TestAction>/#) { testPlans.output + $0.output } + } + + try scheme.write(to: url, atomically: true, encoding: .utf8) +} diff --git a/Tools/XcodeGen/postGenCommand.sh b/Tools/XcodeGen/postGenCommand.sh index 806da8fb2..454a2f93f 100644 --- a/Tools/XcodeGen/postGenCommand.sh +++ b/Tools/XcodeGen/postGenCommand.sh @@ -4,3 +4,6 @@ # Move file header template in project shared data folder cp IDETemplateMacros.plist ../../ElementX.xcodeproj/xcshareddata/ + +# Rewrite schemes the way Xcode saves them +swift normalizeSchemes.swift ../../ElementX.xcodeproj/xcshareddata/xcschemes