Skip to content

Commit 4ecc0a8

Browse files
itamaroclaude
andcommitted
Link only the bootstrap modules into the build helper programs
Programs/_freeze_module and _bootstrap_python were linked with $(LIBRARY_OBJS_OMIT_FROZEN), i.e. every static module from all Setup files (Setup.local, Setup.stdlib with MODULE_BUILDTYPE=static, ...). That makes it impossible for a builtin module to depend on anything the helpers produce: a frozen module header, Modules/getpath.o, or generated sources. A Setup.local module including a header generated by $(FREEZE_MODULE) is a dependency cycle ("Circular ... dependency dropped"), and one referencing a getpath.o symbol fails to link, since _freeze_module links Modules/getpath_noop.o instead. CPython itself needed a workaround for the latter: a stub of _Py_Get_Getpath_CodeObject() in getpath_noop.c for _testinternalcapi in MODULE_BUILDTYPE=static builds. Mirror what PCbuild/_freeze_module.vcxproj already does on Windows (a fixed source list plus PC/config_minimal.c): link the helpers with the core objects plus the modules of Modules/Setup.bootstrap only. * makesetup: new "-b Setup" option marking a Setup file as the bootstrap modules. Their objects and libraries are substituted for _BOOTSTRAP_MODOBJS_ / _BOOTSTRAP_MODLIBS_, and a second table, config_bootstrap.c, is generated with only those modules. A bootstrap module overridden by an earlier rule (Setup.local) keeps the overriding static module for the helpers too. * Makefile.pre.in: BOOTSTRAP_LIBRARY_OBJS (core + Modules/config_bootstrap.o + $(BOOTSTRAP_MODOBJS)) replaces LIBRARY_OBJS_OMIT_FROZEN in the _freeze_module and _bootstrap_python links. getbuildinfo gets a separate object for the helpers, as its prerequisites include $(MODOBJS). * configure.ac / configure: pass -b Modules/Setup.bootstrap and move config_bootstrap.c into Modules/ (configure hand-edited in sync). * Modules/getpath_noop.c: drop the now unnecessary stub. * Tools/c-analyzer: whitelist _PyImport_Inittab in the generated Modules/config_bootstrap.c, as for Modules/config.c. The final libpython/python link is unchanged. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YD8kPnXaAMJZk2LvSHPXYA
1 parent 7b12a41 commit 4ecc0a8

7 files changed

Lines changed: 135 additions & 48 deletions

File tree

‎.gitignore‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ Modules/Setup.config
102102
Modules/Setup.local
103103
Modules/Setup.stdlib
104104
Modules/config.c
105+
Modules/config_bootstrap.c
105106
Modules/ld_so_aix
106107
Programs/_freeze_module
107108
Programs/_testembed

‎Makefile.pre.in‎

Lines changed: 54 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,13 @@ MODSHARED_NAMES= _MODSHARED_NAMES_
2525
MODDISABLED_NAMES= _MODDISABLED_NAMES_
2626
MODOBJS= _MODOBJS_
2727
MODLIBS= _MODLIBS_
28+
# Objects and libraries of the bootstrap modules (Modules/Setup.bootstrap)
29+
# only. These are the only static modules linked into the build helper
30+
# programs (Programs/_freeze_module, _bootstrap_python), so that other
31+
# builtin modules (Modules/Setup.local, MODULE_BUILDTYPE=static) may depend
32+
# on products of those helpers (frozen modules, getpath).
33+
BOOTSTRAP_MODOBJS= _BOOTSTRAP_MODOBJS_
34+
BOOTSTRAP_MODLIBS= _BOOTSTRAP_MODLIBS_
2835

2936
# === Variables set by configure
3037
VERSION= @VERSION@
@@ -359,11 +366,14 @@ COVERAGE_REPORT_OPTIONS=--rc lcov_branch_coverage=1 --branch-coverage --title "C
359366

360367
##########################################################################
361368
# Modules
362-
MODULE_OBJS= \
363-
Modules/config.o \
369+
MODULE_OBJS_COMMON= \
364370
Modules/main.o \
365371
Modules/gcmodule.o
366372

373+
MODULE_OBJS= \
374+
Modules/config.o \
375+
$(MODULE_OBJS_COMMON)
376+
367377
IO_H= Modules/_io/_iomodule.h
368378

369379
IO_OBJS= \
@@ -614,6 +624,19 @@ LIBRARY_OBJS= \
614624
Modules/getpath.o \
615625
Python/frozen.o
616626

627+
# objects that get linked into the build helper programs
628+
# (Programs/_freeze_module, _bootstrap_python): the core, plus the bootstrap
629+
# modules only (see BOOTSTRAP_MODOBJS). Does not include frozen modules nor
630+
# getpath, which the helpers are used to build.
631+
BOOTSTRAP_LIBRARY_OBJS= \
632+
Modules/getbuildinfo_bootstrap.o \
633+
$(PARSER_OBJS) \
634+
$(OBJECT_OBJS) \
635+
$(PYTHON_OBJS) \
636+
Modules/config_bootstrap.o \
637+
$(MODULE_OBJS_COMMON) \
638+
$(BOOTSTRAP_MODOBJS)
639+
617640
LINK_PYTHON_OBJS=@LINK_PYTHON_OBJS@
618641

619642
##########################################################################
@@ -1613,7 +1636,7 @@ Modules/Setup.bootstrap: $(srcdir)/Modules/Setup.bootstrap.in config.status
16131636
Modules/Setup.stdlib: $(srcdir)/Modules/Setup.stdlib.in config.status
16141637
./config.status $@
16151638

1616-
Makefile Modules/config.c: Makefile.pre \
1639+
Makefile Modules/config.c Modules/config_bootstrap.c: Makefile.pre \
16171640
$(srcdir)/Modules/config.c.in \
16181641
$(MAKESETUP) \
16191642
$(srcdir)/Modules/Setup \
@@ -1624,9 +1647,9 @@ Makefile Modules/config.c: Makefile.pre \
16241647
-s Modules \
16251648
Modules/Setup.local \
16261649
Modules/Setup.stdlib \
1627-
Modules/Setup.bootstrap \
1650+
-b Modules/Setup.bootstrap \
16281651
$(srcdir)/Modules/Setup
1629-
@mv config.c Modules
1652+
@mv config.c config_bootstrap.c Modules
16301653
@echo "The Makefile was updated, you may need to re-run make."
16311654

16321655
.PHONY: regen-test-frozenmain
@@ -1661,9 +1684,9 @@ BOOTSTRAP_HEADERS = \
16611684

16621685
Programs/_bootstrap_python.o: Programs/_bootstrap_python.c $(BOOTSTRAP_HEADERS) $(PYTHON_HEADERS)
16631686

1664-
_bootstrap_python: $(LIBRARY_OBJS_OMIT_FROZEN) Programs/_bootstrap_python.o Modules/getpath.o Modules/Setup.local
1665-
$(LINKCC) $(PY_LDFLAGS_NOLTO) -o $@ $(LIBRARY_OBJS_OMIT_FROZEN) \
1666-
Programs/_bootstrap_python.o Modules/getpath.o $(LIBS) $(MODLIBS) $(SYSLIBS)
1687+
_bootstrap_python: $(BOOTSTRAP_LIBRARY_OBJS) Programs/_bootstrap_python.o Modules/getpath.o Modules/Setup.local
1688+
$(LINKCC) $(PY_LDFLAGS_NOLTO) -o $@ $(BOOTSTRAP_LIBRARY_OBJS) \
1689+
Programs/_bootstrap_python.o Modules/getpath.o $(LIBS) $(BOOTSTRAP_MODLIBS) $(SYSLIBS)
16671690
# Dummy pybuilddir.txt is needed for _bootstrap_python to be runnable
16681691
@echo "none" > ./pybuilddir.txt
16691692

@@ -1681,6 +1704,9 @@ _bootstrap_python: $(LIBRARY_OBJS_OMIT_FROZEN) Programs/_bootstrap_python.o Modu
16811704
#
16821705
# Standard build process:
16831706
# 1) compile minimal core objects for Py_Compile*() and PyMarshal_Write*().
1707+
# Only the bootstrap modules (Modules/Setup.bootstrap) are part of these
1708+
# objects (see BOOTSTRAP_LIBRARY_OBJS), so other builtin modules may
1709+
# depend on the output of the following steps.
16841710
# 2) build Programs/_freeze_module binary.
16851711
# 3) create frozen module headers for importlib and getpath.
16861712
# 4) build _bootstrap_python binary.
@@ -1753,8 +1779,8 @@ Programs/_freeze_module.o: Programs/_freeze_module.c Makefile
17531779

17541780
Modules/getpath_noop.o: $(srcdir)/Modules/getpath_noop.c Makefile
17551781

1756-
Programs/_freeze_module: Programs/_freeze_module.o Modules/getpath_noop.o $(LIBRARY_OBJS_OMIT_FROZEN)
1757-
$(LINKCC) $(PY_CORE_LDFLAGS) -o $@ Programs/_freeze_module.o Modules/getpath_noop.o $(LIBRARY_OBJS_OMIT_FROZEN) $(LIBS) $(MODLIBS) $(SYSLIBS)
1782+
Programs/_freeze_module: Programs/_freeze_module.o Modules/getpath_noop.o $(BOOTSTRAP_LIBRARY_OBJS)
1783+
$(LINKCC) $(PY_CORE_LDFLAGS) -o $@ Programs/_freeze_module.o Modules/getpath_noop.o $(BOOTSTRAP_LIBRARY_OBJS) $(LIBS) $(BOOTSTRAP_MODLIBS) $(SYSLIBS)
17581784

17591785
# We manually freeze getpath.py rather than through freeze_modules
17601786
Python/frozen_modules/getpath.h: Modules/getpath.py $(FREEZE_MODULE_BOOTSTRAP_DEPS)
@@ -1907,11 +1933,24 @@ Modules/getbuildinfo.o: $(PARSER_OBJS) \
19071933
$(MODOBJS) \
19081934
$(DTRACE_OBJS) \
19091935
$(srcdir)/Modules/getbuildinfo.c
1910-
$(CC) -c $(PY_CORE_CFLAGS) \
1936+
$(CC) -c $(PY_CORE_CFLAGS) $(GETBUILDINFO_CFLAGS) \
1937+
-o $@ $(srcdir)/Modules/getbuildinfo.c
1938+
1939+
# Separate object for the build helper programs: it must not depend on
1940+
# $(MODOBJS), which may in turn depend on the helpers' output.
1941+
Modules/getbuildinfo_bootstrap.o: $(PARSER_OBJS) \
1942+
$(OBJECT_OBJS) \
1943+
$(PYTHON_OBJS) \
1944+
$(MODULE_OBJS_COMMON) \
1945+
$(BOOTSTRAP_MODOBJS) \
1946+
$(srcdir)/Modules/getbuildinfo.c
1947+
$(CC) -c $(PY_CORE_CFLAGS) $(GETBUILDINFO_CFLAGS) \
1948+
-o $@ $(srcdir)/Modules/getbuildinfo.c
1949+
1950+
GETBUILDINFO_CFLAGS= \
19111951
-DGITVERSION="\"`LC_ALL=C $(GITVERSION)`\"" \
19121952
-DGITTAG="\"`LC_ALL=C $(GITTAG)`\"" \
1913-
-DGITBRANCH="\"`LC_ALL=C $(GITBRANCH)`\"" \
1914-
-o $@ $(srcdir)/Modules/getbuildinfo.c
1953+
-DGITBRANCH="\"`LC_ALL=C $(GITBRANCH)`\""
19151954

19161955
Modules/getpath.o: $(srcdir)/Modules/getpath.c Python/frozen_modules/getpath.h Makefile $(PYTHON_HEADERS)
19171956
$(CC) -c $(PY_CORE_CFLAGS) -DPYTHONPATH='"$(PYTHONPATH)"' \
@@ -2279,7 +2318,7 @@ regen-slots: Python/slots.toml
22792318
$(PYTHON_FOR_REGEN) $(srcdir)/Tools/build/generate_slots.py \
22802319
--generate-all
22812320

2282-
$(LIBRARY_OBJS) $(MODOBJS) Programs/python.o $(PLATFORM_MAIN_OBJS): $(PYTHON_HEADERS)
2321+
$(LIBRARY_OBJS) $(MODOBJS) Modules/config_bootstrap.o Programs/python.o $(PLATFORM_MAIN_OBJS): $(PYTHON_HEADERS)
22832322

22842323

22852324
######################################################################
@@ -3365,7 +3404,7 @@ clean: clean-profile clean-jit-stencils
33653404
clobber: clean
33663405
-rm -f $(BUILDPYTHON) $(LIBRARY) $(LDLIBRARY) $(DLLLIBRARY) \
33673406
tags TAGS \
3368-
config.cache config.log pyconfig.h Modules/config.c
3407+
config.cache config.log pyconfig.h Modules/config.c Modules/config_bootstrap.c
33693408
-rm -rf build platform
33703409
-rm -rf $(PYTHONFRAMEWORKDIR)
33713410
-rm -rf Platforms/Apple/iOS/Frameworks

‎Modules/getpath_noop.c‎

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,10 @@
11
/* Implements the getpath API for compiling with no functionality */
22

33
#include "Python.h"
4-
#include "pycore_initconfig.h" // _Py_Get_Getpath_CodeObject()
54
#include "pycore_pathconfig.h" // _PyConfig_InitPathConfig()
65

76
PyStatus
87
_PyConfig_InitPathConfig(PyConfig *config, int compute_path_config)
98
{
109
return PyStatus_Error("path configuration is unsupported");
1110
}
12-
13-
/* Used by _testinternalcapi, which is linked into Programs/_freeze_module
14-
when extension modules are built in (MODULE_BUILDTYPE=static). */
15-
PyObject *
16-
_Py_Get_Getpath_CodeObject(void)
17-
{
18-
PyErr_SetString(PyExc_RuntimeError,
19-
"path configuration is unsupported");
20-
return NULL;
21-
}

‎Modules/makesetup‎

Lines changed: 75 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,22 @@ set -e
44
# Convert templates into Makefile and config.c, based on the module
55
# definitions found in the file Setup.
66
#
7-
# Usage: makesetup [-s dir] [-c file] [-m file] [Setup] ... [-n [Setup] ...]
7+
# Usage: makesetup [-s dir] [-c file] [-m file] [Setup] ... [-b Setup] ...
8+
# [-n [Setup] ...]
89
#
910
# Options:
1011
# -s directory: alternative source directory (default .)
1112
# -l directory: library source directory (default derived from $0)
1213
# -c file: alternative config.c template (default $libdir/config.c.in)
13-
# -c -: don't write config.c
14+
# -c -: don't write config.c (nor config_bootstrap.c)
1415
# -m file: alternative Makefile template (default ./Makefile.pre)
1516
# -m -: don't write Makefile
1617
#
1718
# Remaining arguments are one or more Setup files (default ./Setup).
19+
# The Setup file following a -b option holds the *bootstrap* modules:
20+
# the static modules that the build helper programs (_freeze_module,
21+
# _bootstrap_python) are linked with. They are also regular modules of
22+
# the final build.
1823
# Setup files after a -n option are used for their variables, modules
1924
# and libraries but not for their .o files.
2025
#
@@ -28,6 +33,9 @@ set -e
2833
# + insert 'extern PyObject* PyInit_<module>(void);' before MARKER 1
2934
# + insert '{"<module>", PyInit_<module>},' before MARKER 2
3035
#
36+
# Copying config.c.in to config_bootstrap.c (only with a -b Setup file):
37+
# - same as config.c, but only for the modules of the -b Setup file
38+
#
3139
# Copying Makefile.pre to Makefile:
3240
# - insert an identifying comment at the start
3341
# - replace _MODBUILT_NAMES_ by the list of *static* and *shared* modules
@@ -37,6 +45,8 @@ set -e
3745
# - replace _MODOBJS_ by the list of objects from Setup (except for
3846
# Setup files after a -n option)
3947
# - replace _MODLIBS_ by the list of libraries from Setup
48+
# - replace _BOOTSTRAP_MODOBJS_ and _BOOTSTRAP_MODLIBS_ by the objects and
49+
# libraries of the -b Setup file
4050
# - for each object file mentioned in Setup, append a rule
4151
# '<file>.o: <file>.c; <build commands>' to the end of the Makefile
4252
# - for each module mentioned in Setup, append a rule
@@ -47,13 +57,14 @@ set -e
4757
# Loop over command line options
4858
usage='
4959
usage: makesetup [-s srcdir] [-l libdir] [-c config.c.in] [-m Makefile.pre]
50-
[Setup] ... [-n [Setup] ...]'
60+
[Setup] ... [-b Setup] ... [-n [Setup] ...]'
5161
srcdir='.'
5262
libdir=''
5363
config=''
5464
makepre=''
5565
noobjects=''
5666
doconfig=yes
67+
bootstrap=
5768
while :
5869
do
5970
case $1 in
@@ -93,7 +104,8 @@ do
93104
echo '' # Add a linebreak so we don't choke on files missing EOL.
94105
case $i in
95106
-n) echo '*noobjects*';;
96-
*) echo '*doconfig*'; cat "$i";;
107+
-b) echo '*bootstrap*';;
108+
*) echo '*doconfig*'; cat "$i"; echo ''; echo '*nobootstrap*';;
97109
esac
98110
done |
99111
sed -e 's/[ ]*#.*//' -e '/^[ ]*$/d' |
@@ -114,6 +126,10 @@ sed -e 's/[ ]*#.*//' -e '/^[ ]*$/d' |
114126
LIBS=
115127
LOCALLIBS=
116128
BASELIBS=
129+
BOOTMODS=
130+
BOOTOBJS=
131+
BOOTLIBS=
132+
havebootstrap=
117133
while read line
118134
do
119135
# to handle backslashes for sh's that don't automatically
@@ -136,6 +152,8 @@ sed -e 's/[ ]*#.*//' -e '/^[ ]*$/d' |
136152
noobjects=yes;
137153
continue;;
138154
'*doconfig*') doconfig=yes; continue;;
155+
'*bootstrap*') bootstrap=yes; havebootstrap=yes; continue;;
156+
'*nobootstrap*') bootstrap=; continue;;
139157
'*static*') doconfig=yes; continue;;
140158
'*noconfig*') doconfig=no; continue;;
141159
'*shared*') doconfig=no; continue;;
@@ -204,6 +222,16 @@ sed -e 's/[ ]*#.*//' -e '/^[ ]*$/d' |
204222
# Detected multiple rules for a module, first rule wins. This
205223
# allows users to disable modules in Setup.local.
206224
echo 1>&2 "maksetup: '$mod' was handled by previous rule."
225+
case $bootstrap in
226+
yes)
227+
# A bootstrap module overridden by an earlier rule
228+
# (e.g. in Setup.local): the build helpers get the
229+
# overriding static module, if any.
230+
eval "BOOTMODS=\"\$BOOTMODS \${STATICMOD_$mod}\""
231+
eval "BOOTOBJS=\"\$BOOTOBJS \${STATICOBJS_$mod}\""
232+
eval "BOOTLIBS=\"\$BOOTLIBS \${STATICLIBS_$mod}\""
233+
;;
234+
esac
207235
continue 2;;
208236
esac
209237
CONFIGURED="$CONFIGURED,${mod},"
@@ -213,6 +241,12 @@ sed -e 's/[ ]*#.*//' -e '/^[ ]*$/d' |
213241
LIBS="$LIBS $libs"
214242
MODS="$MODS $mods"
215243
BUILT="$BUILT $mods"
244+
case $bootstrap in
245+
yes)
246+
BOOTLIBS="$BOOTLIBS $libs"
247+
BOOTMODS="$BOOTMODS $mods"
248+
;;
249+
esac
216250
;;
217251
no)
218252
BUILT="$BUILT $mods"
@@ -265,7 +299,20 @@ sed -e 's/[ ]*#.*//' -e '/^[ ]*$/d' |
265299
echo "$rule" >>$rulesf
266300
done
267301
case $doconfig in
268-
yes) OBJS="$OBJS $objs";;
302+
yes)
303+
OBJS="$OBJS $objs"
304+
case $bootstrap in
305+
yes) BOOTOBJS="$BOOTOBJS $objs";;
306+
esac
307+
# Remember static modules so that a later bootstrap rule
308+
# for the same module can pick them up (see above).
309+
for mod in $mods
310+
do
311+
eval "STATICMOD_$mod='$mod'"
312+
eval "STATICOBJS_$mod='$objs'"
313+
eval "STATICLIBS_$mod='$libs'"
314+
done
315+
;;
269316
esac
270317
for mod in $mods
271318
do
@@ -295,24 +342,31 @@ sed -e 's/[ ]*#.*//' -e '/^[ ]*$/d' |
295342
DEFS="BASEMODLIBS=$BASELIBS$NL$DEFS"
296343
DEFS="LOCALMODLIBS=$LOCALLIBS$NL$DEFS"
297344

298-
EXTDECLS=
299-
INITBITS=
300-
for mod in $MODS
301-
do
302-
EXTDECLS="${EXTDECLS}extern PyObject* PyInit_$mod(void);$NL"
303-
INITBITS="${INITBITS} {\"$mod\", PyInit_$mod},$NL"
304-
done
305-
306-
307-
case $config in
308-
-) ;;
309-
*) sed -e "
345+
# Generate a config.c from the template for the given modules
346+
genconfig() {
347+
out=$1; shift
348+
EXTDECLS=
349+
INITBITS=
350+
for mod
351+
do
352+
EXTDECLS="${EXTDECLS}extern PyObject* PyInit_$mod(void);$NL"
353+
INITBITS="${INITBITS} {\"$mod\", PyInit_$mod},$NL"
354+
done
355+
sed -e "
310356
1i$NL/* Generated automatically from $config by makesetup. */
311357
/MARKER 1/i$NL$EXTDECLS
312358
313359
/MARKER 2/i$NL$INITBITS
314360
315-
" $config >config.c
361+
" $config >$out
362+
}
363+
364+
case $config in
365+
-) ;;
366+
*) genconfig config.c $MODS
367+
case $havebootstrap in
368+
yes) genconfig config_bootstrap.c $BOOTMODS;;
369+
esac
316370
;;
317371
esac
318372

@@ -331,6 +385,9 @@ sed -e 's/[ ]*#.*//' -e '/^[ ]*$/d' |
331385
echo "s%_MODBUILT_NAMES_%$BUILT%" >>$sedf
332386
echo "s%_MODSHARED_NAMES_%$BUILT_SHARED%" >>$sedf
333387
echo "s%_MODDISABLED_NAMES_%$DISABLED%" >>$sedf
388+
# _BOOTSTRAP_* first: _MODOBJS_ is a substring of _BOOTSTRAP_MODOBJS_
389+
echo "s%_BOOTSTRAP_MODOBJS_%$BOOTOBJS%" >>$sedf
390+
echo "s%_BOOTSTRAP_MODLIBS_%$BOOTLIBS%" >>$sedf
334391
echo "s%_MODOBJS_%$OBJS%" >>$sedf
335392
echo "s%_MODLIBS_%$LIBS%" >>$sedf
336393
echo "/Definitions added by makesetup/r $sedr" >>$sedf

‎Tools/c-analyzer/cpython/ignored.tsv‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,7 @@ Modules/cmathmodule.c - sinh_special_values -
314314
Modules/cmathmodule.c - sqrt_special_values -
315315
Modules/cmathmodule.c - tanh_special_values -
316316
Modules/config.c - _PyImport_Inittab -
317+
Modules/config_bootstrap.c - _PyImport_Inittab -
317318
Modules/faulthandler.c - faulthandler_handlers -
318319
Modules/getnameinfo.c - gni_afdl -
319320
Modules/hmacmodule.c - py_hmac_static_hinfo -

0 commit comments

Comments
 (0)