Skip to content

Commit febf54b

Browse files
bpo-46712: Do not Regen Deep-Frozen Modules before Generating Global Objects (gh-32061)
We have to run "make regen-deepfreeze" before running Tools/scripts/generate-global-objects.py; otherwise we will miss any changes to global objects in deep-frozen modules (which aren't committed in the repo). However, building $(PYTHON_FOR_FREEZE) fails if one of its source files had a global object (e.g. via _Py_ID(...)) added or removed, without generate-global-objects.py running first. So "make regen-global-objects" would sometimes fail. We solve this by running generate-global-objects.py before *and* after "make regen-deepfreeze". To speed things up and cut down on noise, we also avoid updating the global objects files if there are no changes to them. https://bugs.python.org/issue46712
1 parent 21412d0 commit febf54b

File tree

2 files changed

+36
-12
lines changed

2 files changed

+36
-12
lines changed

Makefile.pre.in

+6-1
Original file line numberDiff line numberDiff line change
@@ -1179,7 +1179,12 @@ regen-importlib: regen-frozen
11791179
# Global objects
11801180

11811181
.PHONY: regen-global-objects
1182-
regen-global-objects: regen-deepfreeze $(srcdir)/Tools/scripts/generate_global_objects.py
1182+
regen-global-objects: $(srcdir)/Tools/scripts/generate_global_objects.py
1183+
$(PYTHON_FOR_REGEN) $(srcdir)/Tools/scripts/generate_global_objects.py
1184+
@# Run one more time after deepfreezing, to catch any globals added
1185+
@# there. This is necessary because the deep-frozen code isn't
1186+
@# commited to the repo.
1187+
$(MAKE) regen-deepfreeze
11831188
$(PYTHON_FOR_REGEN) $(srcdir)/Tools/scripts/generate_global_objects.py
11841189

11851190
############################################################################

Tools/scripts/generate_global_objects.py

+30-11
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import contextlib
22
import glob
3+
import io
34
import os.path
45
import re
56
import sys
@@ -123,6 +124,7 @@ def iter_global_strings():
123124
varname, string = m.groups()
124125
yield varname, string, filename, lno, line
125126

127+
126128
def iter_to_marker(lines, marker):
127129
for line in lines:
128130
if line.rstrip() == marker:
@@ -165,6 +167,19 @@ def block(self, prefix, suffix="", *, continuation=None):
165167
self.write("}" + suffix)
166168

167169

170+
@contextlib.contextmanager
171+
def open_for_changes(filename, orig):
172+
"""Like open() but only write to the file if it changed."""
173+
outfile = io.StringIO()
174+
yield outfile
175+
text = outfile.getvalue()
176+
if text != orig:
177+
with open(filename, 'w', encoding='utf-8') as outfile:
178+
outfile.write(text)
179+
else:
180+
print(f'# not changed: {filename}')
181+
182+
168183
#######################################
169184
# the global objects
170185

@@ -177,13 +192,15 @@ def generate_global_strings(identifiers, strings):
177192

178193
# Read the non-generated part of the file.
179194
with open(filename) as infile:
180-
before = ''.join(iter_to_marker(infile, START))[:-1]
181-
for _ in iter_to_marker(infile, END):
182-
pass
183-
after = infile.read()[:-1]
195+
orig = infile.read()
196+
lines = iter(orig.rstrip().splitlines())
197+
before = '\n'.join(iter_to_marker(lines, START))
198+
for _ in iter_to_marker(lines, END):
199+
pass
200+
after = '\n'.join(lines)
184201

185202
# Generate the file.
186-
with open(filename, 'w', encoding='utf-8') as outfile:
203+
with open_for_changes(filename, orig) as outfile:
187204
printer = Printer(outfile)
188205
printer.write(before)
189206
printer.write(START)
@@ -202,7 +219,6 @@ def generate_global_strings(identifiers, strings):
202219
with printer.block('struct', ' latin1[128];'):
203220
printer.write("PyCompactUnicodeObject _latin1;")
204221
printer.write("uint8_t _data[2];")
205-
206222
printer.write(END)
207223
printer.write(after)
208224

@@ -227,13 +243,15 @@ def generate_runtime_init(identifiers, strings):
227243

228244
# Read the non-generated part of the file.
229245
with open(filename) as infile:
230-
before = ''.join(iter_to_marker(infile, START))[:-1]
231-
for _ in iter_to_marker(infile, END):
232-
pass
233-
after = infile.read()[:-1]
246+
orig = infile.read()
247+
lines = iter(orig.rstrip().splitlines())
248+
before = '\n'.join(iter_to_marker(lines, START))
249+
for _ in iter_to_marker(lines, END):
250+
pass
251+
after = '\n'.join(lines)
234252

235253
# Generate the file.
236-
with open(filename, 'w', encoding='utf-8') as outfile:
254+
with open_for_changes(filename, orig) as outfile:
237255
printer = Printer(outfile)
238256
printer.write(before)
239257
printer.write(START)
@@ -286,6 +304,7 @@ def get_identifiers_and_strings() -> 'tuple[set[str], dict[str, str]]':
286304
raise ValueError(f'string mismatch for {name!r} ({string!r} != {strings[name]!r}')
287305
return identifiers, strings
288306

307+
289308
#######################################
290309
# the script
291310

0 commit comments

Comments
 (0)