summaryrefslogtreecommitdiffstats
path: root/scripts/update_pbxproj.py
diff options
context:
space:
mode:
authorivar <i@oiee.no>2026-05-06 21:51:33 +0200
committerivar <i@oiee.no>2026-05-06 21:51:33 +0200
commit49826892ad7b91179d3721410318b0972d01be9f (patch)
treeea4e45f94b8ce203b685769826d9accb81fa7b96 /scripts/update_pbxproj.py
parent54dd55db8c19667939536e18535ac9c45817e442 (diff)
downloadsolverv-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.py210
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")