summaryrefslogtreecommitdiffstats
path: root/scripts/update_pbxproj.py
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/update_pbxproj.py')
-rw-r--r--scripts/update_pbxproj.py177
1 files changed, 177 insertions, 0 deletions
diff --git a/scripts/update_pbxproj.py b/scripts/update_pbxproj.py
new file mode 100644
index 0000000..b213f4e
--- /dev/null
+++ b/scripts/update_pbxproj.py
@@ -0,0 +1,177 @@
+#!/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
+
+Run from the 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
+)
+
+# ── 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)
+
+# 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():]
+
+# ── 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)
+
+# ── 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)
+
+# ── 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)
+
+# ── 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)
+
+# ── 11. Add Shared group hierarchy ──
+models_grp_uid = uid()
+utils_grp_uid = uid()
+shared_grp_uid = uid()
+
+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
+)
+
+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'
+ 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,
+ 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
+ )
+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.")