summaryrefslogtreecommitdiffstats
path: root/scripts/update_pbxproj.py
blob: b213f4e4919ca008da4da61440f935c1f368d2bb (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
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.")