1- # gh-116869: Build a basic C test extension to check that the Python C API
2- # does not emit C compiler warnings.
1+ # gh-116869: Build a C/C++ test extension to check that the Python C API does
2+ # not emit compiler warnings.
33#
44# The Python C API must be compatible with building
55# with the -Werror=declaration-after-statement compiler flag.
66
77import os .path
8+ import platform
89import shlex
910import shutil
1011import subprocess
12+ import sys
13+ import sysconfig
1114import unittest
1215from test import support
16+ from test .support import os_helper
1317
1418
19+ if not support .has_subprocess_support :
20+ raise unittest .SkipTest ("requires subprocess support" )
21+
22+
23+ SOURCE_DIR = os .path .dirname (__file__ )
1524SOURCES = [
16- os .path .join (os .path .dirname (__file__ ), 'extension.c' ),
25+ os .path .join (SOURCE_DIR , 'extension.c' ),
26+ os .path .join (SOURCE_DIR , 'extension.cpp' ),
27+ os .path .join (SOURCE_DIR , 'setup.py' ),
1728]
18- SETUP = os . path . join ( os . path . dirname ( __file__ ), 'setup.py' )
29+ MSVC = support . MS_WINDOWS
1930
2031
2132# With MSVC on a debug build, the linker fails with: cannot open file
2233# 'python311.lib', it should look 'python311_d.lib'.
23- @unittest .skipIf (support . MS_WINDOWS and support .Py_DEBUG ,
34+ @unittest .skipIf (MSVC and support .Py_DEBUG ,
2435 'test fails on Windows debug build' )
2536# Building and running an extension in clang sanitizing mode is not
2637# straightforward
3243@support .requires_resource ('cpu' )
3344class BaseTests :
3445 TEST_INTERNAL_C_API = False
46+ LANGUAGE = None
3547
36- # Default build with no options
37- def test_build (self ):
38- self .check_build ('_test_cext' )
39-
40- def check_build (self , extension_name , std = None , limited = False ):
41- venv_dir = 'env'
42- with support .setup_venv_with_pip_setuptools (venv_dir ) as python_exe :
43- self ._check_build (extension_name , python_exe ,
44- std = std , limited = limited )
48+ def check_build (self , extension_name , std = None , limited = False ,
49+ extra_cflags = None ):
50+ if self .LANGUAGE == 'C++' and not std and sys .platform == 'darwin' :
51+ # Old Apple clang++ default C++ std is gnu++98, use C++11 instead
52+ std = 'c++11'
4553
46- def _check_build (self , extension_name , python_exe , std , limited ):
4754 pkg_dir = 'pkg'
4855 os .mkdir (pkg_dir )
49- shutil .copy (SETUP , os .path .join (pkg_dir , os .path .basename (SETUP )))
56+ self .addCleanup (os_helper .rmtree , pkg_dir )
57+
5058 for source in SOURCES :
5159 dest = os .path .join (pkg_dir , os .path .basename (source ))
5260 shutil .copy (source , dest )
5361
5462 def run_cmd (operation , cmd ):
5563 env = os .environ .copy ()
64+ env ['CPYTHON_TEST_EXT_NAME' ] = extension_name
65+ env ['CPYTHON_TEST_LANG' ] = self .LANGUAGE
5666 if std :
5767 env ['CPYTHON_TEST_STD' ] = std
5868 if limited :
5969 env ['CPYTHON_TEST_LIMITED' ] = '1'
60- env ['CPYTHON_TEST_EXT_NAME' ] = extension_name
61- env ['TEST_INTERNAL_C_API' ] = str (int (self .TEST_INTERNAL_C_API ))
70+ if MSVC and sysconfig .is_python_build ():
71+ env ['CPYTHON_TEST_EXTRA_INCDIRS' ] = os .path .split (sysconfig .get_config_h_filename ())[0 ]
72+ env ['CPYTHON_TEST_EXTRA_LIBDIRS' ] = os .path .split (sys .executable )[0 ]
73+ env ['CPYTHON_TEST_INTERNAL_C_API' ] = str (int (self .TEST_INTERNAL_C_API ))
74+ if extra_cflags :
75+ env ['CPYTHON_TEST_EXTRA_CFLAGS' ] = extra_cflags
6276 if support .verbose :
6377 print ('Run:' , ' ' .join (map (shlex .quote , cmd )))
6478 subprocess .run (cmd , check = True , env = env )
@@ -75,6 +89,7 @@ def run_cmd(operation, cmd):
7589 f"{ operation } failed with exit code { proc .returncode } " )
7690
7791 # Build and install the C extension
92+ python_exe = PYTHON_EXE
7893 cmd = [python_exe , '-X' , 'dev' ,
7994 '-m' , 'pip' , 'install' , '--no-build-isolation' ,
8095 os .path .abspath (pkg_dir )]
@@ -91,37 +106,102 @@ def run_cmd(operation, cmd):
91106 '-c' , 'pass' ]
92107 run_cmd ('Reference run' , cmd )
93108
94- # Import the C extension
109+ # Import the C/C++ extension
95110 cmd = [python_exe ,
96111 '-X' , 'dev' ,
97112 '-X' , 'showrefcount' ,
98113 '-c' , f"import { extension_name } " ]
99114 run_cmd ('Import' , cmd )
100115
101116
102- class TestPublicCAPI (BaseTests , unittest .TestCase ):
103- @support .requires_gil_enabled ('incompatible with Free Threading' )
104- def test_build_limited (self ):
105- self .check_build ('_test_limited_cext' , limited = True )
117+ class TestPublicC (BaseTests , unittest .TestCase ):
118+ LANGUAGE = 'C'
106119
107- @support .requires_gil_enabled ('broken for now with Free Threading' )
108- def test_build_limited_c11 (self ):
109- self .check_build ('_test_limited_c11_cext' , limited = True , std = 'c11' )
110-
111- def test_build_c11 (self ):
112- self .check_build ('_test_c11_cext' , std = 'c11' )
120+ # Default build with no options
121+ def test_build (self ):
122+ self .check_build ('_test_cext' )
113123
114- @unittest .skipIf (support . MS_WINDOWS , "MSVC doesn't support /std:c99" )
124+ @unittest .skipIf (MSVC , "MSVC doesn't support /std:c99" )
115125 def test_build_c99 (self ):
116126 # In public docs, we say C API is compatible with C11. However,
117127 # in practice we do maintain C99 compatibility in public headers.
118128 # Please ask the C API WG before adding a new C11-only feature.
119- self .check_build ('_test_c99_cext' , std = 'c99' )
129+ self .check_build ('_test_cext_c99' , std = 'c99' )
130+
131+ def test_build_c11 (self ):
132+ self .check_build ('_test_cext_c11' , std = 'c11' )
133+
134+ def test_build_limited (self ):
135+ self .check_build ('_test_cext_limited' , limited = True )
136+
137+ def test_build_limited_c11 (self ):
138+ self .check_build ('_test_cext_limited_c11' , limited = True , std = 'c11' )
139+
140+
141+ class TestPublicCpp (BaseTests , unittest .TestCase ):
142+ LANGUAGE = 'C++'
120143
144+ def test_build (self ):
145+ self .check_build ('_test_cppext' )
146+
147+ def test_build_cpp03 (self ):
148+ # In public docs, we say C API is compatible with C++11. However,
149+ # in practice we do maintain C++03 compatibility in public headers.
150+ # Please ask the C API WG before adding a new C++11-only feature.
151+ self .check_build ('_test_cppext_cpp03' , std = 'c++03' )
152+
153+ @unittest .skipIf (MSVC , "MSVC doesn't support /std:c++11" )
154+ def test_build_cpp11 (self ):
155+ self .check_build ('_test_cppext_cpp11' , std = 'c++11' )
156+
157+ # Only test C++14 on MSVC.
158+ # On s390x RHEL7, GCC 4.8.5 doesn't support C++14.
159+ @unittest .skipIf (not MSVC , "need MSVC" )
160+ def test_build_cpp14 (self ):
161+ self .check_build ('_test_cppext_cpp14' , std = 'c++14' )
162+
163+ # Test that headers compile with Intel asm syntax, which may conflict
164+ # with inline assembly in free-threading headers that use AT&T syntax.
165+ @unittest .skipIf (MSVC , "MSVC doesn't support -masm=intel" )
166+ @unittest .skipUnless (platform .machine () in ('x86_64' , 'i686' , 'AMD64' ),
167+ "x86-specific flag" )
168+ def test_build_intel_asm (self ):
169+ self .check_build ('_test_cppext_intel_asm' , extra_cflags = '-masm=intel' )
170+
171+ def test_build_limited (self ):
172+ self .check_build ('_test_cppext_limited' , limited = True )
121173
122- class TestInteralCAPI (BaseTests , unittest .TestCase ):
174+ def test_build_limited_cpp03 (self ):
175+ self .check_build ('_test_cppext_limited_cpp03' , std = 'c++03' , limited = True )
176+
177+
178+ class TestInteralC (BaseTests , unittest .TestCase ):
179+ LANGUAGE = 'C'
123180 TEST_INTERNAL_C_API = True
124181
182+ # Default build with no options
183+ def test_build (self ):
184+ self .check_build ('_test_cext_internal' )
185+
186+
187+ class TestInteralCpp (BaseTests , unittest .TestCase ):
188+ LANGUAGE = 'C++'
189+ TEST_INTERNAL_C_API = True
190+
191+ def test_build (self ):
192+ self .check_build ('_test_cppext_internal' )
193+
194+
195+ @support .requires_venv_with_pip ()
196+ def setUpModule ():
197+ global VENV_CONTEXT , PYTHON_EXE
198+ VENV_CONTEXT = support .setup_venv_with_pip_setuptools ('env' )
199+ PYTHON_EXE = VENV_CONTEXT .__enter__ ()
200+
201+
202+ def tearDownModule ():
203+ VENV_CONTEXT .__exit__ (None , None , None )
204+
125205
126206if __name__ == "__main__" :
127207 unittest .main ()
0 commit comments