Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
54ba2c0
gh-158948: Add auto-generated Module/getbuildinfo.h
vstinner Oct 6, 2026
508e3ca
Fix and reforamt generate_getbuildinfo.py
vstinner Oct 7, 2026
8e7fe03
Look for pyconfig.h in the current working directory
vstinner Oct 7, 2026
06cc348
Fix out-of-tree build
vstinner Oct 7, 2026
7c10d0b
Short comment
vstinner Oct 7, 2026
8c2dcce
Update PCbuild/_freeze_module.vcxproj
vstinner Oct 7, 2026
7882331
Update Tools/c-analyzer/cpython/ignored.tsv
vstinner Oct 7, 2026
22d1a6b
Don't redefine PY_VERSION
vstinner Oct 7, 2026
61ddc42
Remove useless getbuildinfo.h dependencies
vstinner Oct 7, 2026
1ae6b29
Add _getcompiler project on Windows
vstinner Oct 7, 2026
cd00cdb
Temporary workaround for cross-compilation
vstinner Oct 7, 2026
9a56153
Update PCbuild/pcbuild.proj
vstinner Oct 7, 2026
eab1b0e
Fix build when there is not .git/ directory (test_tools)
vstinner Oct 7, 2026
a0780d4
Enhance generate_getbuildinfo.py
vstinner Oct 7, 2026
0c8ad79
Run directly the compiler (--version) if _getcompiler failed
vstinner Oct 7, 2026
0d50e2e
Reuse sysconfig parsers
vstinner Oct 7, 2026
98519e1
Simplify _getcompiler build command
vstinner Oct 7, 2026
05a9779
Fix typo
vstinner Oct 7, 2026
11cd830
Strip URL from verbose Clang version
vstinner Oct 7, 2026
dc6d476
Strip '[ and ']' from COMPILER and _Py_COMPILER
vstinner Oct 7, 2026
ad75107
Tolerate "cc --version" failure, use "C" compiler by default
vstinner Oct 7, 2026
4281ae9
Cleanup get_compiler() code
vstinner Oct 8, 2026
d6fd2d4
Shorter clang version; add tests
vstinner Oct 8, 2026
aa9a4aa
Fix test
vstinner Oct 8, 2026
b036e72
Keep the vendor (Android or Apple) in Clang version
vstinner Oct 8, 2026
1d9bb17
Revert unrelated changes
vstinner Oct 8, 2026
ebaadbc
Log the truncated compiler string
vstinner Oct 8, 2026
2c9bafb
Support old sysconfig versions
vstinner Oct 8, 2026
8431d21
Fix Makefile (_getcompiler) for BSD make
vstinner Oct 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,11 @@ Modules/Setup.config
Modules/Setup.local
Modules/Setup.stdlib
Modules/config.c
# Generated by Tools/build/generate_getbuildinfo.py
Modules/getbuildinfo.h
Modules/ld_so_aix
Programs/_freeze_module
Programs/_getcompiler
Programs/_testembed
PC/python_nt*.h
PC/pythonnt_rc*.h
Expand Down
2 changes: 0 additions & 2 deletions Include/internal/pycore_pylifecycle.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ extern int _Py_IsLocaleCoercionTarget(const char *ctype_loc);

/* Various one-time initializers */

extern PyStatus _Py_GetVersion_Init(void);
extern PyStatus _PyFaulthandler_Init(int enable);
extern PyObject * _PyBuiltin_Init(PyInterpreterState *interp);
extern int _PyBuiltin_InitPythonFunctions(PyObject *dict);
Expand All @@ -43,7 +42,6 @@ extern PyStatus _PyDateTime_InitTypes(PyInterpreterState *interp);

/* Various internal finalizers */

extern void _Py_GetVersion_Fini(void);
extern int _PySignal_Init(int install_signal_handlers);
extern void _PySignal_Fini(void);

Expand Down
195 changes: 195 additions & 0 deletions Lib/test/test_tools/test_getbuildinfo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
"""Test Tools/build/generate_getbuildinfo.py."""

import locale
import os
import subprocess
import sys
import textwrap
import unittest
from unittest import mock
from test import support
from test.support import os_helper
from test import test_tools


test_tools.skip_if_missing("build")

with test_tools.imports_under_tool('build'):
import generate_getbuildinfo

SRC_DIR = os.path.dirname(test_tools.toolsdir)
SCRIPT_NAME = 'generate_getbuildinfo.py'


class TestGetBuildInfo(unittest.TestCase):
def test_get_py_version(self):
with os_helper.change_cwd(SRC_DIR):
PY_VERSION = generate_getbuildinfo.get_py_version()

# "./python -V" writes "Python {PY_VERSION}"
proc = subprocess.run([sys.executable, '-V'],
stdout=subprocess.PIPE,
text=True, check=True)
python_version = proc.stdout.rstrip()
self.assertEqual(f'Python {PY_VERSION}', python_version)

def test_get_date_time(self):
# Test get_date_time() with SOURCE_DATE_EPOCH env var
def check(timestamp, date, time):
with os_helper.EnvironmentVarGuard() as env:
env['SOURCE_DATE_EPOCH'] = str(timestamp)
result = generate_getbuildinfo.get_date_time()

self.assertEqual(result, (date, time))

check(0, 'Jan 1 1970', '00:00:00')
check(1791419852, 'Oct 8 2026', '00:37:32')

def test_get_build_info(self):
get_build_info = generate_getbuildinfo.get_build_info

with os_helper.EnvironmentVarGuard() as env:
env['SOURCE_DATE_EPOCH'] = '0'
date_time = 'Jan 1 1970, 00:00:00'
# all set
self.assertEqual(get_build_info('tag', 'branch', 'version'),
(f'tag:version, {date_time}', 'tag'))
# missing version
self.assertEqual(get_build_info('tag', 'branch', ''),
(f'tag, {date_time}', 'tag'))
# Missing tag
self.assertEqual(get_build_info('', 'branch', 'version'),
(f'branch:version, {date_time}', 'branch'))
# undefined tag
self.assertEqual(get_build_info('undefined', 'branch', 'version'),
(f'branch:version, {date_time}', 'branch'))
# all empty
self.assertEqual(get_build_info('', '', ''),
(f'main, {date_time}', 'main'))

def test_get_compiler(self):
getcompiler = os.path.join('Programs', '_getcompiler')
MOCK_COMPILER = 'MOCK_COMPILER'
CC = 'gcc -std=c11'

for hostrunner in ('', 'node'):
for use_cc in (False, True):
with (mock.patch.object(generate_getbuildinfo,
'get_hostrunner',
return_value=hostrunner),
mock.patch.object(generate_getbuildinfo,
'get_makefile_cc',
return_value=CC),
mock.patch.object(generate_getbuildinfo,
'run_command') as mock_run_command):
if use_cc:
mock_run_command.side_effect = ('', MOCK_COMPILER)
else:
mock_run_command.return_value = MOCK_COMPILER
compiler = generate_getbuildinfo._get_compiler()
self.assertEqual(compiler, MOCK_COMPILER)
if hostrunner:
cmd = [hostrunner, getcompiler]
else:
cmd = [getcompiler]
if use_cc:
self.assertEqual(mock_run_command.call_args_list,
[
mock.call(cmd, check=False),
mock.call(['gcc', '-std=c11', '--version'], check=False),
]
)
else:
mock_run_command.assert_called_once_with(cmd, check=False)

def test_compact_compiler(self):
get_compiler = generate_getbuildinfo.get_compiler
compact_compiler = generate_getbuildinfo.compact_compiler

def check(compiler, expected):
self.assertEqual(compact_compiler(compiler), expected)

def check_unchanged(compiler):
self.assertEqual(compact_compiler(compiler), compiler)

compiler = (
'Android (13691557, +pgo, +bolt, +lto, +mlgo, based on r522817d) '
'clang version 18.0.4 '
'(https://android.googlesource.com/toolchain/llvm-project'
' d8003a456d14a3deb8054cdaa529ffbf02d9b262)'
)
check(compiler, 'Android Clang 18.0.4')

compiler = (
'Clang 18.0.4 '
'(https://android.googlesource.com/toolchain/llvm-project'
' d8003a456d14a3deb8054cdaa529ffbf02d9b262)'
)
check(compiler, 'Clang 18.0.4')

compiler = (
'Clang 24.0.0git '
'(https:/github.com/llvm/llvm-project'
' a06d9165905ce89b5ffef2bbb84c886d60a9b8bf)'
)
check(compiler, 'Clang 24.0.0git')

check('Clang 21.0.0 (clang-2100.1.1.101)',
'Clang 21.0.0')
check('Apple clang version 21.0.0 (clang-2100.1.1.101)',
'Apple Clang 21.0.0')
check('Custom Vendor (something) Clang 22.1.0+dev',
'Custom Vendor Clang 22.1.0+dev')

check_unchanged('GCC 16.2.1 20260819 (Red Hat 16.2.1-2)')
check_unchanged('GCC 15.2.0')
check_unchanged('MSC v.1951 32 bit (Intel)')

with support.captured_stdout() as stdout:
compiler = get_compiler('Clang 21.0.0 (clang-2100.1.1.101)')
self.assertEqual(compiler, '[Clang 21.0.0]')
self.assertEqual(stdout.getvalue(),
f"{SCRIPT_NAME}: Truncate compiler string 'Clang 21.0.0 "
"(clang-2100.1.1.101)' to 'Clang 21.0.0'\n")

def test_functional(self):
old_locale = locale.setlocale(locale.LC_ALL)
self.addCleanup(locale.setlocale, locale.LC_ALL, old_locale)

filename = 'output.txt'
self.addCleanup(os.unlink, filename)

argv = [
'program',
'--output', filename,
'--platform', 'SET_PLATFORM',
'--git-version', 'VERSION',
'--git-tag', 'TAG',
'--git-branch', 'BRANCH',
'--compiler', 'COMPILER',
'--free-threading', '1',
]
with support.swap_attr(sys, 'argv', argv):
with os_helper.EnvironmentVarGuard() as env:
env['SOURCE_DATE_EPOCH'] = str(1791419852)
with support.captured_stdout() as stdout:
generate_getbuildinfo.main()
self.assertEqual(stdout.getvalue(), f'{SCRIPT_NAME}: {filename} updated\n')

with open(filename) as fp:
output = fp.read()
expected = textwrap.dedent('''
// Header file auto-generated by Tools/build/generate_getbuildinfo.py

#define PLATFORM "SET_PLATFORM"
#define COMPILER "[COMPILER]"
#define GIT_VERSION "VERSION"
#define GIT_IDENTIFIER "TAG"
#define BUILD_INFO "TAG:VERSION, Oct 8 2026, 00:37:32"
#define VERSION "3.16.0a0 free-threading build (TAG:VERSION, Oct 8 2026, 00:37:32) [COMPILER]"
''').lstrip()
self.assertEqual(output, expected)


if __name__ == "__main__":
unittest.main()
33 changes: 14 additions & 19 deletions Makefile.pre.in
Original file line number Diff line number Diff line change
Expand Up @@ -461,10 +461,6 @@ PYTHON_OBJS= \
Python/gc_free_threading.o \
Python/gc_gil.o \
Python/getargs.o \
Python/getcompiler.o \
Python/getcopyright.o \
Python/getplatform.o \
Python/getversion.o \
Python/ceval_gil.o \
Python/hamt.o \
Python/hashtable.o \
Expand Down Expand Up @@ -1617,6 +1613,9 @@ regen-re: $(BUILDPYTHON)
Programs/_testembed: Programs/_testembed.o $(LINK_PYTHON_DEPS)
$(LINKCC) $(PY_CORE_EXE_LDFLAGS) $(LINKFORSHARED) -o $@ Programs/_testembed.o $(LINK_PYTHON_OBJS) $(LIBS) $(MODLIBS) $(SYSLIBS)

Programs/_getcompiler: $(srcdir)/Programs/_getcompiler.c
$(LINKCC) $(PY_CPPFLAGS) -o $@ $(srcdir)/Programs/_getcompiler.c

############################################################################
# "Bootstrap Python" used to run Programs/_freeze_module.py

Expand Down Expand Up @@ -1867,18 +1866,17 @@ regen-all: regen-cases regen-slots \
############################################################################
# Special rules for object files

Modules/getbuildinfo.o: $(PARSER_OBJS) \
$(OBJECT_OBJS) \
$(PYTHON_OBJS) \
$(MODULE_OBJS) \
$(MODOBJS) \
$(DTRACE_OBJS) \
$(srcdir)/Modules/getbuildinfo.c
$(CC) -c $(PY_CORE_CFLAGS) \
-DGITVERSION="\"`LC_ALL=C $(GITVERSION)`\"" \
-DGITTAG="\"`LC_ALL=C $(GITTAG)`\"" \
-DGITBRANCH="\"`LC_ALL=C $(GITBRANCH)`\"" \
-o $@ $(srcdir)/Modules/getbuildinfo.c
Modules/getbuildinfo.h: $(srcdir)/Tools/build/generate_getbuildinfo.py Programs/_getcompiler
$(PYTHON_FOR_REGEN) $(srcdir)/Tools/build/generate_getbuildinfo.py \
-o $@ \
--platform="$(MACHDEP)" \
--git-version="`LC_ALL=C $(GITVERSION)`" \
--git-tag="`LC_ALL=C $(GITTAG)`" \
--git-branch="`LC_ALL=C $(GITBRANCH)`"

Modules/getbuildinfo.o: $(srcdir)/Modules/getbuildinfo.c Modules/getbuildinfo.h
# Add local Modules/ include directory to get Modules/getbuildinfo.h
$(CC) -c $(PY_CORE_CFLAGS) -IModules/ -o $@ $(srcdir)/Modules/getbuildinfo.c

Modules/getpath.o: $(srcdir)/Modules/getpath.c Python/frozen_modules/getpath.h Makefile $(PYTHON_HEADERS)
$(CC) -c $(PY_CORE_CFLAGS) -DPYTHONPATH='"$(PYTHONPATH)"' \
Expand Down Expand Up @@ -2028,9 +2026,6 @@ regen-sre:

Python/compile.o Python/codegen.o Python/symtable.o Python/ast_unparse.o Python/ast.o Python/future.o: $(srcdir)/Include/internal/pycore_ast.h $(srcdir)/Include/internal/pycore_ast.h

Python/getplatform.o: $(srcdir)/Python/getplatform.c
$(CC) -c $(PY_CORE_CFLAGS) -DPLATFORM='"$(MACHDEP)"' -o $@ $(srcdir)/Python/getplatform.c

Python/importdl.o: $(srcdir)/Python/importdl.c
$(CC) -c $(PY_CORE_CFLAGS) -I$(DLINCLDIR) -o $@ $(srcdir)/Python/importdl.c

Expand Down
Loading
Loading