]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.12] gh-127906: Backport test_cppext changes from the main branch (#127915)
authorVictor Stinner <vstinner@python.org>
Fri, 13 Dec 2024 12:51:06 +0000 (13:51 +0100)
committerGitHub <noreply@github.com>
Fri, 13 Dec 2024 12:51:06 +0000 (13:51 +0100)
Lib/test/test_cppext/__init__.py
Lib/test/test_cppext/extension.cpp
Lib/test/test_cppext/setup.py

index f02a823bd22176136ec0a06ab44d9001a638d7da..efd79448c6610453af07c7cd2eb7aab768891667 100644 (file)
@@ -1,45 +1,52 @@
 # gh-91321: Build a basic C++ test extension to check that the Python C API is
 # compatible with C++ and does not emit C++ compiler warnings.
 import os.path
+import shlex
 import shutil
-import sys
-import unittest
 import subprocess
-import sysconfig
+import unittest
 from test import support
-from test.support import os_helper
 
 
 SOURCE = os.path.join(os.path.dirname(__file__), 'extension.cpp')
 SETUP = os.path.join(os.path.dirname(__file__), 'setup.py')
 
 
+# With MSVC on a debug build, the linker fails with: cannot open file
+# 'python311.lib', it should look 'python311_d.lib'.
+@unittest.skipIf(support.MS_WINDOWS and support.Py_DEBUG,
+                 'test fails on Windows debug build')
+# Building and running an extension in clang sanitizing mode is not
+# straightforward
+@support.skip_if_sanitizer('test does not work with analyzing builds',
+                           address=True, memory=True, ub=True, thread=True)
+# the test uses venv+pip: skip if it's not available
+@support.requires_venv_with_pip()
 @support.requires_subprocess()
+@support.requires_resource('cpu')
 class TestCPPExt(unittest.TestCase):
-    @support.requires_resource('cpu')
-    def test_build_cpp11(self):
-        self.check_build(False, '_testcpp11ext')
+    def test_build(self):
+        self.check_build('_testcppext')
 
-    @support.requires_resource('cpu')
     def test_build_cpp03(self):
-        self.check_build(True, '_testcpp03ext')
+        self.check_build('_testcpp03ext', std='c++03')
+
+    @unittest.skipIf(support.MS_WINDOWS, "MSVC doesn't support /std:c++11")
+    def test_build_cpp11(self):
+        self.check_build('_testcpp11ext', std='c++11')
+
+    # Only test C++14 on MSVC.
+    # On s390x RHEL7, GCC 4.8.5 doesn't support C++14.
+    @unittest.skipIf(not support.MS_WINDOWS, "need Windows")
+    def test_build_cpp14(self):
+        self.check_build('_testcpp14ext', std='c++14')
 
-    # With MSVC, the linker fails with: cannot open file 'python311.lib'
-    # https://github.com/python/cpython/pull/32175#issuecomment-1111175897
-    @unittest.skipIf(support.MS_WINDOWS, 'test fails on Windows')
-    # Building and running an extension in clang sanitizing mode is not
-    # straightforward
-    @unittest.skipIf(
-        '-fsanitize' in (sysconfig.get_config_var('PY_CFLAGS') or ''),
-        'test does not work with analyzing builds')
-    # the test uses venv+pip: skip if it's not available
-    @support.requires_venv_with_pip()
-    def check_build(self, std_cpp03, extension_name):
+    def check_build(self, extension_name, std=None):
         venv_dir = 'env'
         with support.setup_venv_with_pip_setuptools_wheel(venv_dir) as python_exe:
-            self._check_build(std_cpp03, extension_name, python_exe)
+            self._check_build(extension_name, python_exe, std=std)
 
-    def _check_build(self, std_cpp03, extension_name, python_exe):
+    def _check_build(self, extension_name, python_exe, std):
         pkg_dir = 'pkg'
         os.mkdir(pkg_dir)
         shutil.copy(SETUP, os.path.join(pkg_dir, os.path.basename(SETUP)))
@@ -47,10 +54,11 @@ class TestCPPExt(unittest.TestCase):
 
         def run_cmd(operation, cmd):
             env = os.environ.copy()
-            env['CPYTHON_TEST_CPP_STD'] = 'c++03' if std_cpp03 else 'c++11'
+            if std:
+                env['CPYTHON_TEST_CPP_STD'] = std
             env['CPYTHON_TEST_EXT_NAME'] = extension_name
             if support.verbose:
-                print('Run:', ' '.join(cmd))
+                print('Run:', ' '.join(map(shlex.quote, cmd)))
                 subprocess.run(cmd, check=True, env=env)
             else:
                 proc = subprocess.run(cmd,
@@ -59,6 +67,7 @@ class TestCPPExt(unittest.TestCase):
                                       stderr=subprocess.STDOUT,
                                       text=True)
                 if proc.returncode:
+                    print('Run:', ' '.join(map(shlex.quote, cmd)))
                     print(proc.stdout, end='')
                     self.fail(
                         f"{operation} failed with exit code {proc.returncode}")
@@ -67,6 +76,8 @@ class TestCPPExt(unittest.TestCase):
         cmd = [python_exe, '-X', 'dev',
                '-m', 'pip', 'install', '--no-build-isolation',
                os.path.abspath(pkg_dir)]
+        if support.verbose:
+            cmd.append('-v')
         run_cmd('Install', cmd)
 
         # Do a reference run. Until we test that running python
index 90669b10cb2c6dfaa702328666c75fdc9d4ae298..ab485b629b778841cfb228022079dffcd8627e0d 100644 (file)
@@ -8,10 +8,8 @@
 
 #include "Python.h"
 
-#if __cplusplus >= 201103
-#  define NAME _testcpp11ext
-#else
-#  define NAME _testcpp03ext
+#ifndef MODULE_NAME
+#  error "MODULE_NAME macro must be defined"
 #endif
 
 #define _STR(NAME) #NAME
@@ -160,7 +158,7 @@ PyType_Slot VirtualPyObject_Slots[] = {
 };
 
 PyType_Spec VirtualPyObject_Spec = {
-    /* .name */ STR(NAME) ".VirtualPyObject",
+    /* .name */ STR(MODULE_NAME) ".VirtualPyObject",
     /* .basicsize */ sizeof(VirtualPyObject),
     /* .itemsize */ 0,
     /* .flags */ Py_TPFLAGS_DEFAULT,
@@ -227,6 +225,10 @@ _testcppext_exec(PyObject *module)
     if (!result) return -1;
     Py_DECREF(result);
 
+    // test Py_BUILD_ASSERT() and Py_BUILD_ASSERT_EXPR()
+    Py_BUILD_ASSERT(sizeof(int) == sizeof(unsigned int));
+    assert(Py_BUILD_ASSERT_EXPR(sizeof(int) == sizeof(unsigned int)) == 0);
+
     return 0;
 }
 
@@ -240,7 +242,7 @@ PyDoc_STRVAR(_testcppext_doc, "C++ test extension.");
 
 static struct PyModuleDef _testcppext_module = {
     PyModuleDef_HEAD_INIT,  // m_base
-    STR(NAME),  // m_name
+    STR(MODULE_NAME),  // m_name
     _testcppext_doc,  // m_doc
     0,  // m_size
     _testcppext_methods,  // m_methods
@@ -254,7 +256,7 @@ static struct PyModuleDef _testcppext_module = {
 #define FUNC_NAME(NAME) _FUNC_NAME(NAME)
 
 PyMODINIT_FUNC
-FUNC_NAME(NAME)(void)
+FUNC_NAME(MODULE_NAME)(void)
 {
     return PyModuleDef_Init(&_testcppext_module);
 }
index c7ba1efb4dd05a4af3fe6dce221568aa16f115bc..d97b238b8d147726b09790ef824c9fd3d97ac381 100644 (file)
@@ -1,8 +1,8 @@
 # gh-91321: Build a basic C++ test extension to check that the Python C API is
 # compatible with C++ and does not emit C++ compiler warnings.
 import os
+import platform
 import shlex
-import sys
 import sysconfig
 from test import support
 
@@ -10,6 +10,7 @@ from setuptools import setup, Extension
 
 
 SOURCE = 'extension.cpp'
+
 if not support.MS_WINDOWS:
     # C++ compiler flags for GCC and clang
     CPPFLAGS = [
@@ -19,34 +20,77 @@ if not support.MS_WINDOWS:
         '-Werror',
     ]
 else:
-    # Don't pass any compiler flag to MSVC
-    CPPFLAGS = []
+    # MSVC compiler flags
+    CPPFLAGS = [
+        # Display warnings level 1 to 4
+        '/W4',
+        # Treat all compiler warnings as compiler errors
+        '/WX',
+    ]
 
 
 def main():
     cppflags = list(CPPFLAGS)
-    std = os.environ["CPYTHON_TEST_CPP_STD"]
-    name = os.environ["CPYTHON_TEST_EXT_NAME"]
+    std = os.environ.get("CPYTHON_TEST_CPP_STD", "")
+    module_name = os.environ["CPYTHON_TEST_EXT_NAME"]
 
-    cppflags = [*CPPFLAGS, f'-std={std}']
+    cppflags = list(CPPFLAGS)
+    cppflags.append(f'-DMODULE_NAME={module_name}')
+
+    # Add -std=STD or /std:STD (MSVC) compiler flag
+    if std:
+        if support.MS_WINDOWS:
+            cppflags.append(f'/std:{std}')
+        else:
+            cppflags.append(f'-std={std}')
 
     # gh-105776: When "gcc -std=11" is used as the C++ compiler, -std=c11
     # option emits a C++ compiler warning. Remove "-std11" option from the
     # CC command.
     cmd = (sysconfig.get_config_var('CC') or '')
     if cmd is not None:
+        if support.MS_WINDOWS:
+            std_prefix = '/std'
+        else:
+            std_prefix = '-std'
         cmd = shlex.split(cmd)
-        cmd = [arg for arg in cmd if not arg.startswith('-std=')]
+        cmd = [arg for arg in cmd if not arg.startswith(std_prefix)]
         cmd = shlex.join(cmd)
         # CC env var overrides sysconfig CC variable in setuptools
         os.environ['CC'] = cmd
 
-    cpp_ext = Extension(
-        name,
+    # On Windows, add PCbuild\amd64\ to include and library directories
+    include_dirs = []
+    library_dirs = []
+    if support.MS_WINDOWS:
+        srcdir = sysconfig.get_config_var('srcdir')
+        machine = platform.uname().machine
+        pcbuild = os.path.join(srcdir, 'PCbuild', machine)
+        if os.path.exists(pcbuild):
+            # pyconfig.h is generated in PCbuild\amd64\
+            include_dirs.append(pcbuild)
+            # python313.lib is generated in PCbuild\amd64\
+            library_dirs.append(pcbuild)
+            print(f"Add PCbuild directory: {pcbuild}")
+
+    # Display information to help debugging
+    for env_name in ('CC', 'CFLAGS', 'CPPFLAGS'):
+        if env_name in os.environ:
+            print(f"{env_name} env var: {os.environ[env_name]!r}")
+        else:
+            print(f"{env_name} env var: <missing>")
+    print(f"extra_compile_args: {cppflags!r}")
+
+    ext = Extension(
+        module_name,
         sources=[SOURCE],
         language='c++',
-        extra_compile_args=cppflags)
-    setup(name='internal' + name, version='0.0', ext_modules=[cpp_ext])
+        extra_compile_args=cppflags,
+        include_dirs=include_dirs,
+        library_dirs=library_dirs)
+    setup(name=f'internal_{module_name}',
+          version='0.0',
+          ext_modules=[ext])
 
 
 if __name__ == "__main__":