diff options
| author | ivar <i@oiee.no> | 2026-05-06 21:51:33 +0200 |
|---|---|---|
| committer | ivar <i@oiee.no> | 2026-05-06 21:51:33 +0200 |
| commit | 49826892ad7b91179d3721410318b0972d01be9f (patch) | |
| tree | ea4e45f94b8ce203b685769826d9accb81fa7b96 /scripts/update_pbxproj.py | |
| parent | 54dd55db8c19667939536e18535ac9c45817e442 (diff) | |
| download | solverv-49826892ad7b91179d3721410318b0972d01be9f.tar.xz solverv-49826892ad7b91179d3721410318b0972d01be9f.zip | |
feat: use PBXFileSystemSynchronizedRootGroup for Shared/ in both targets
Diffstat (limited to 'scripts/update_pbxproj.py')
| -rw-r--r-- | scripts/update_pbxproj.py | 210 |
1 files changed, 71 insertions, 139 deletions
diff --git a/scripts/update_pbxproj.py b/scripts/update_pbxproj.py index b213f4e..d73697a 100644 --- a/scripts/update_pbxproj.py +++ b/scripts/update_pbxproj.py @@ -1,177 +1,109 @@ #!/usr/bin/env python3 """ -Updates Solverv.xcodeproj/project.pbxproj to: - - Add Shared/{Models,Utilities} file references for 5 shared files - - Add those files to both targets' PBXSourcesBuildPhase - - Remove old duplicate file references and build entries from both targets - - Add a Shared group in the project navigator +Replaces old-style PBXGroup+PBXFileReference approach for Shared/ with +PBXFileSystemSynchronizedRootGroup (the Xcode 16 native approach). -Run from the project root: python3 scripts/update_pbxproj.py +Steps: + 1. Remove explicit build file entries for all 5 shared files from both build phases + 2. Remove explicit PBXBuildFile entries + 3. Remove explicit PBXFileReference entries + 4. Remove PBXGroup entries for Shared/Models/Utilities + 5. Add a PBXFileSystemSynchronizedRootGroup for Shared/ + 6. Add it to both targets' fileSystemSynchronizedGroups lists + +Run from project root: python3 scripts/update_pbxproj.py """ import re import secrets PROJ = "Solverv.xcodeproj/project.pbxproj" -MODELS_FILES = ["Season.swift", "SolsticeEvent.swift", "SolsticeData.swift"] -UTIL_FILES = ["AppGroupManager.swift", "SunTimes.swift"] -ALL_FILES = MODELS_FILES + UTIL_FILES - def uid(): return secrets.token_hex(12).upper() with open(PROJ) as f: src = f.read() -# ── 1. Find existing file-reference UUIDs (expect 2 per name: one per target) ── -old_ref_uids = {} -for name in ALL_FILES: - pat = r'([0-9A-Fa-f]{24})\s*/\*\s*' + re.escape(name) + r'\s*\*/' - found = re.findall(pat, src) - old_ref_uids[name] = [u.upper() for u in found] - if len(found) != 2: - print(f"WARNING: expected 2 PBXFileReference entries for {name}, found {len(found)}: {found}") - -# ── 2. Find existing build-file UUIDs (expect 2 per name: one per target) ── -old_build_uids = {} -for name in ALL_FILES: - pat = r'([0-9A-Fa-f]{24})\s*/\*\s*' + re.escape(name) + r'\s+in\s+Sources\s*\*/' - found = re.findall(pat, src) - old_build_uids[name] = [u.upper() for u in found] - if len(found) != 2: - print(f"WARNING: expected 2 PBXBuildFile entries for {name}, found {len(found)}: {found}") - -# ── 3. Generate new UUIDs ── -new_ref_uid = {name: uid() for name in ALL_FILES} -new_build_uid = {name: [uid(), uid()] for name in ALL_FILES} # [target0, target1] - -# ── 4. Insert new PBXFileReference entries ── -new_refs = "\n".join( - f'\t\t{new_ref_uid[name]} /* {name} */ = ' - f'{{isa = PBXFileReference; lastKnownFileType = sourcecode.swift; ' - f'path = {name}; sourceTree = "<group>"; }};' - for name in ALL_FILES -) + "\n" - -src = src.replace( - "/* Begin PBXFileReference section */", - "/* Begin PBXFileReference section */\n" + new_refs, - 1 -) - -# ── 5. Insert new PBXBuildFile entries ── -new_builds = "\n".join( - f'\t\t{new_build_uid[name][i]} /* {name} in Sources */ = ' - f'{{isa = PBXBuildFile; fileRef = {new_ref_uid[name]} /* {name} */; }};' - for name in ALL_FILES - for i in range(2) -) + "\n" - -src = src.replace( - "/* Begin PBXBuildFile section */", - "/* Begin PBXBuildFile section */\n" + new_builds, - 1 -) +# ── UUIDs to remove (explicit PBXBuildFile entries) ── +# Two per file: (main_app_uid, widget_uid) +build_file_uids = [ + ("A4D1E5F62BE4C16BF9C35920", "BDE41CA8E12EF58194F0FB28"), # Season.swift + ("0602B5968563FDCC6AAE214B", "C1829841EA43782BD52878E9"), # SolsticeEvent.swift + ("58EC7B50BC5232D031299280", "DB32B658D259B49C522786C8"), # SolsticeData.swift + ("63374A5CD6AAFBBA4A5E87AC", "6FFB4853DA8AF00282F20F96"), # AppGroupManager.swift + ("594A94559E2251F892BC158B", "90AEA7599D196072837AE994"), # SunTimes.swift +] -# ── 6. Add new build files to both PBXSourcesBuildPhase sections ── -phases = list(re.finditer( - r'(isa = PBXSourcesBuildPhase;.*?files = \()(.*?)(\);)', - src, re.DOTALL -)) -if len(phases) != 2: - print(f"ERROR: expected 2 PBXSourcesBuildPhase, found {len(phases)}. Aborting.") - exit(1) +# PBXFileReference UUIDs to remove +file_ref_uids = [ + "BAAD0F6249013EFDE6CF3BDB", # Season.swift + "A53B1F64353769F3F3D52DCC", # SolsticeEvent.swift + "56A3A45E5A25BDBCC47A23EC", # SolsticeData.swift + "3B12784AD1DE5013F9E3B677", # AppGroupManager.swift + "5C4C2D0816DA7D3E6E6A4358", # SunTimes.swift +] -# Replace in reverse order so offsets stay valid -for i, phase in reversed(list(enumerate(phases))): - additions = "".join( - f'\t\t\t\t{new_build_uid[name][i]} /* {name} in Sources */,\n' - for name in ALL_FILES - ) - new_phase = phase.group(1) + phase.group(2) + additions + phase.group(3) - src = src[:phase.start()] + new_phase + src[phase.end():] +# PBXGroup UUIDs to remove +group_uids = [ + "0BFB6DC6E4F437012CF2990E", # Models + "5F48ADA595F6B3DE3DFE2A32", # Utilities + "0EEEC5869B3AB7A2A2154C10", # Shared +] -# ── 7. Remove old build-file entries from PBXSourcesBuildPhase ── -for name in ALL_FILES: - for bu in old_build_uids[name]: - src = re.sub(r'\t{3,4}' + bu + r'\s*/\*[^*]*\*/,\n', '', src) +# ── 1. Remove explicit entries from both PBXSourcesBuildPhase sections ── +all_build_uids = [u for pair in build_file_uids for u in pair] +for bu in all_build_uids: + src = re.sub(r'\t{3,4}' + bu + r'\s*/\*[^*]*\*/,\n', '', src) -# ── 8. Remove old PBXBuildFile entries ── -for name in ALL_FILES: - for bu in old_build_uids[name]: - src = re.sub(r'\t\t' + bu + r'\s*/\*[^*]*\*/\s*=\s*\{[^}]*\};\n', '', src) +# ── 2. Remove PBXBuildFile entries ── +for bu in all_build_uids: + src = re.sub(r'\t\t' + bu + r'\s*/\*[^*]*\*/\s*=\s*\{[^}]*\};\n', '', src) -# ── 9. Remove old PBXFileReference entries ── -for name in ALL_FILES: - for ru in old_ref_uids[name]: - src = re.sub(r'\t\t' + ru + r'\s*/\*[^*]*\*/\s*=\s*\{[^}]*\};\n', '', src) +# ── 3. Remove PBXFileReference entries ── +for ru in file_ref_uids: + src = re.sub(r'\t\t' + ru + r'\s*/\*[^*]*\*/\s*=\s*\{[^}]*\};\n', '', src) -# ── 10. Remove old file refs from PBXGroup children ── -for name in ALL_FILES: - for ru in old_ref_uids[name]: - src = re.sub(r'\t{3,4}' + ru + r'\s*/\*[^*]*\*/,\n', '', src) +# ── 4. Remove PBXGroup entries for Shared/Models/Utilities ── +# These span multiple lines so we need a multi-line match +for gu in group_uids: + src = re.sub(r'\t\t' + gu + r'\s*/\*[^*]*\*/\s*=\s*\{[^}]*\};\n', '', src, flags=re.DOTALL) -# ── 11. Add Shared group hierarchy ── -models_grp_uid = uid() -utils_grp_uid = uid() -shared_grp_uid = uid() +# ── 5. Remove Shared group from main project group children ── +src = re.sub(r'\t{3,4}0EEEC5869B3AB7A2A2154C10\s*/\*[^*]*\*/,\n', '', src) -models_children = "".join( - f'\t\t\t\t{new_ref_uid[n]} /* {n} */,\n' for n in MODELS_FILES -) -utils_children = "".join( - f'\t\t\t\t{new_ref_uid[n]} /* {n} */,\n' for n in UTIL_FILES -) +# ── 6. Add PBXFileSystemSynchronizedRootGroup for Shared/ ── +shared_sync_uid = uid() -new_groups = ( - f'\t\t{models_grp_uid} /* Models */ = {{\n' - f'\t\t\tisa = PBXGroup;\n' - f'\t\t\tchildren = (\n' - f'{models_children}' - f'\t\t\t);\n' - f'\t\t\tpath = Models;\n' - f'\t\t\tsourceTree = "<group>";\n' - f'\t\t}};\n' - f'\t\t{utils_grp_uid} /* Utilities */ = {{\n' - f'\t\t\tisa = PBXGroup;\n' - f'\t\t\tchildren = (\n' - f'{utils_children}' - f'\t\t\t);\n' - f'\t\t\tpath = Utilities;\n' - f'\t\t\tsourceTree = "<group>";\n' - f'\t\t}};\n' - f'\t\t{shared_grp_uid} /* Shared */ = {{\n' - f'\t\t\tisa = PBXGroup;\n' - f'\t\t\tchildren = (\n' - f'\t\t\t\t{models_grp_uid} /* Models */,\n' - f'\t\t\t\t{utils_grp_uid} /* Utilities */,\n' - f'\t\t\t);\n' +new_sync_group = ( + f'\t\t{shared_sync_uid} /* Shared */ = {{\n' + f'\t\t\tisa = PBXFileSystemSynchronizedRootGroup;\n' f'\t\t\tpath = Shared;\n' f'\t\t\tsourceTree = "<group>";\n' f'\t\t}};\n' ) src = src.replace( - "/* Begin PBXGroup section */", - "/* Begin PBXGroup section */\n" + new_groups, + "/* Begin PBXFileSystemSynchronizedRootGroup section */", + "/* Begin PBXFileSystemSynchronizedRootGroup section */\n" + new_sync_group, 1 ) -# Add Shared group as child of the main project group -main_grp_match = re.search(r'mainGroup = ([0-9A-Fa-f]{24})', src) -if main_grp_match: - main_grp_uid = main_grp_match.group(1).upper() - # Find that group's children list and prepend the Shared group - src = re.sub( - r'(' + main_grp_uid + r'\s*/\*[^*]*\*/\s*=\s*\{[^{]*isa = PBXGroup;[^{]*children = \()', - r'\1\n\t\t\t\t' + shared_grp_uid + r' /* Shared */,', - src +# ── 7. Add shared_sync_uid to both targets' fileSystemSynchronizedGroups ── +# Main app target: 1B8629BE2EF0C636005A1C75 +# Widget target: 1B8629D22EF0C656005A1C75 + +for target_uid, existing_sync in [ + ("1B8629BE2EF0C636005A1C75", "1B8629C12EF0C636005A1C75"), # Solverv → Solverv group + ("1B8629D22EF0C656005A1C75", "1B8629D92EF0C656005A1C75"), # Widget → Solsnu.Widget group +]: + src = src.replace( + f'\t\t\t\t{existing_sync} /* ', + f'\t\t\t\t{shared_sync_uid} /* Shared */,\n\t\t\t\t{existing_sync} /* ', + 1 ) -else: - print("WARNING: could not find mainGroup — add Shared group manually in Xcode") with open(PROJ, "w") as f: f.write(src) -print("project.pbxproj updated.") -print("Open Xcode and verify the Shared group appears with all 5 files under both targets.") +print(f"Done. New Shared synchronized root group UID: {shared_sync_uid}") +print("Run: plutil -lint Solverv.xcodeproj/project.pbxproj") |
