Skip to content

Commit 379cf2d

Browse files
authored
Merge pull request #156 from Czaki/master
upgrade macos version for support c++11
2 parents 973ecf5 + b64b106 commit 379cf2d

File tree

9 files changed

+207
-19
lines changed

9 files changed

+207
-19
lines changed

cibuildwheel/__main__.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,8 +201,11 @@ def detect_obsolete_options():
201201
# Check for 'manylinux1' in the 'CIBW_BUILD' and 'CIBW_SKIP' options
202202
for deprecated in ['CIBW_BUILD', 'CIBW_SKIP']:
203203
if deprecated in os.environ and 'manylinux1' in os.environ[deprecated]:
204-
print("Build identifiers with 'manylinux1' been deprecated. Replacing all occurences of 'manylinux1' by 'manylinux' in the option '{}'".format(deprecated))
204+
print("Build identifiers with 'manylinux1' have been deprecated. Replacing all occurences of 'manylinux1' by 'manylinux' in the option '{}'".format(deprecated))
205205
os.environ[deprecated] = os.environ[deprecated].replace('manylinux1', 'manylinux')
206+
if deprecated in os.environ and ("macosx_10_6" in os.environ[deprecated] or "macosx_10_9" in os.environ[deprecated]):
207+
print("Build identifiers with 'macosx_10_6' or 'macosx_10_9' have been deprecated. Replacing all occurences with 'macosx' in the option '{}'".format(deprecated))
208+
os.environ[deprecated] = os.environ[deprecated].replace('macosx_10_6', 'macosx').replace('macosx_10_9', 'macosx')
206209

207210

208211
def print_preamble(platform, build_options):

cibuildwheel/macos.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@
1414
def get_python_configurations(build_selector):
1515
PythonConfiguration = namedtuple('PythonConfiguration', ['version', 'identifier', 'url'])
1616
python_configurations = [
17-
PythonConfiguration(version='2.7', identifier='cp27-macosx_10_6_intel', url='https://www.python.org/ftp/python/2.7.17/python-2.7.17-macosx10.6.pkg'),
18-
PythonConfiguration(version='3.5', identifier='cp35-macosx_10_6_intel', url='https://www.python.org/ftp/python/3.5.4/python-3.5.4-macosx10.6.pkg'),
19-
PythonConfiguration(version='3.6', identifier='cp36-macosx_10_6_intel', url='https://www.python.org/ftp/python/3.6.8/python-3.6.8-macosx10.6.pkg'),
20-
PythonConfiguration(version='3.7', identifier='cp37-macosx_10_6_intel', url='https://www.python.org/ftp/python/3.7.6/python-3.7.6-macosx10.6.pkg'),
21-
PythonConfiguration(version='3.8', identifier='cp38-macosx_10_9_x86_64', url='https://www.python.org/ftp/python/3.8.1/python-3.8.1-macosx10.9.pkg'),
17+
PythonConfiguration(version='2.7', identifier='cp27-macosx_intel', url='https://www.python.org/ftp/python/2.7.17/python-2.7.17-macosx10.6.pkg'),
18+
PythonConfiguration(version='3.5', identifier='cp35-macosx_intel', url='https://www.python.org/ftp/python/3.5.4/python-3.5.4-macosx10.6.pkg'),
19+
PythonConfiguration(version='3.6', identifier='cp36-macosx_intel', url='https://www.python.org/ftp/python/3.6.8/python-3.6.8-macosx10.6.pkg'),
20+
PythonConfiguration(version='3.7', identifier='cp37-macosx_intel', url='https://www.python.org/ftp/python/3.7.5/python-3.7.5-macosx10.6.pkg'),
21+
PythonConfiguration(version='3.8', identifier='cp38-macosx_x86_64', url='https://www.python.org/ftp/python/3.8.0/python-3.8.0-macosx10.9.pkg'),
2222
]
2323

2424
# skip builds as required

docs/cpp_standards.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
---
2+
title: Modern C++ standards
3+
---
4+
5+
Building Python wheels with modern C++ standards (C++11 and later) requires a few tricks.
6+
7+
8+
## Python 2.7 and C++17
9+
10+
The Python 2.7 header files use the `register` keyword, which is [reserved and unused from C+17 onwards](https://en.cppreference.com/w/cpp/keyword/register). Compiling a wheel for Python 2.7 with the C++17 standard is still possible to allow usage of `register` using proper flag `-Wno-register` for gcc/clang and `/wd5033` for MSVC.
11+
12+
## manylinux1 and C++14
13+
The default `manylinux1` image (based on CentOS 5) contains a version of GCC and libstdc++ that only supports C++11 and earlier standards. There are however ways to compile wheels with the C++14 standard (and later): https://github.com/pypa/manylinux/issues/118
14+
15+
`manylinux2010` and `manylinux2014` are newer and support all C++ standards (up to C++17).
16+
17+
## macOS and deployment target versions
18+
19+
OS X/macOS allows you to specify a so-called "deployment target" version that will ensure backwards compatibility with older versions of macOS. One way to do this is by setting the `MACOSX_DEPLOYMENT_TARGET` environment variable. If not set, Python will set this variable to the version the Python distribution itself was compiled on (10.6 or 10.9, for the python.org packages), when creating the wheel.
20+
21+
However, to enable modern C++ standards, the deploment target needs to be set high enough (since older OS X/macOS versions did not have the necessary modern C++ standard library).
22+
23+
To get C++11 and C++14 support, set `MACOSX_DEPLOYMENT_TARGET` to (at least) `"10.9"`.
24+
25+
To get C++17 support, set `MACOSX_DEPLOYMENT_TARGET` to (at least) `"10.13"` or `"10.14"` (macOS 10.13 offers partial C++17 support; e.g., the filesystem header is in experimental, offering `#include <experimental/filesystem>` instead of `#include <filesystem>`; macOS 10.14 has full C++17 support).
26+
27+
For more details see https://en.cppreference.com/w/cpp/compiler_support and https://xcodereleases.com/: Xcode 10 needs macOS 10.13 and Xcode 11 needs macOS 10.14.
28+
29+
## Windows and Python 2.7
30+
31+
Visual C++ for Python 2.7 does not support modern standards of C++. When building on Appveyor, you will need to either use the "Visual Studio 2017" or "Visual Studio 2019" image, but Python 2.7 is not supported on these images - skip it by setting `CIBW_SKIP=cp27-win*`.
32+
33+
There is an optional workaround for this, though: the pybind11 project argues and shows that it is [possible to compile Python 2.7 extension with a newer compiler](https://pybind11.readthedocs.io/en/stable/faq.html#working-with-ancient-visual-studio-2008-builds-on-windows) and has an example project showing how to do this: https://github.com/pybind/python_example. The main catch is that a user might need to install a newer "Microsoft Visual C++ Redistributable", since the newer C++ standard libraries are not included by default with the Python 2.7 installation.

docs/options.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -64,21 +64,21 @@ This option can also be set using the command-line option `--platform`.
6464

6565
> Choose the Python versions to build
6666

67-
Space-separated list of builds to build and skip. Each build has an identifier like `cp27-manylinux_x86_64` or `cp35-macosx_10_6_intel` - you can list specific ones to build and `cibuildwheel` will only build those, and/or list ones to skip and `cibuildwheel` won't try to build them.
67+
Space-separated list of builds to build and skip. Each build has an identifier like `cp27-manylinux_x86_64` or `cp35-macosx_intel` - you can list specific ones to build and `cibuildwheel` will only build those, and/or list ones to skip and `cibuildwheel` won't try to build them.
6868

6969
When both options are specified, both conditions are applied and only builds with a tag that matches `CIBW_BUILD` and does not match `CIBW_SKIP` will be built.
7070

7171
When setting the options, you can use shell-style globbing syntax (as per `fnmatch`). All the build identifiers supported by cibuildwheel are shown below:
7272

7373
<div class="build-id-table-marker"></div>
7474

75-
| | macOS 64bit | macOS 32/64bit | Manylinux 64bit | Manylinux 32bit | Windows 64bit | Windows 32bit |
76-
|------------|-------------------------|------------------------|------------------------|----------------------|-----------------|----------------|
77-
| Python 2.7 | | cp27-macosx_10_6_intel | cp27-manylinux_x86_64 | cp27-manylinux_i686 | cp27-win_amd64 | cp27-win32 |
78-
| Python 3.5 | | cp35-macosx_10_6_intel | cp35-manylinux_x86_64 | cp35-manylinux_i686 | cp35-win_amd64 | cp35-win32 |
79-
| Python 3.6 | | cp36-macosx_10_6_intel | cp36-manylinux_x86_64 | cp36-manylinux_i686 | cp36-win_amd64 | cp36-win32 |
80-
| Python 3.7 | | cp37-macosx_10_6_intel | cp37-manylinux_x86_64 | cp37-manylinux_i686 | cp37-win_amd64 | cp37-win32 |
81-
| Python 3.8 | cp38-macosx_10_9_x86_64 | | cp38-manylinux_x86_64 | cp38-manylinux_i686 | cp38-win_amd64 | cp38-win32 |
75+
| | macOS 64bit | macOS 32/64bit | Manylinux 64bit | Manylinux 32bit | Windows 64bit | Windows 32bit |
76+
|------------|---------------------|--------------------|------------------------|----------------------|-----------------|----------------|
77+
| Python 2.7 | | cp27-macosx_intel | cp27-manylinux_x86_64 | cp27-manylinux_i686 | cp27-win_amd64 | cp27-win32 |
78+
| Python 3.5 | | cp35-macosx_intel | cp35-manylinux_x86_64 | cp35-manylinux_i686 | cp35-win_amd64 | cp35-win32 |
79+
| Python 3.6 | | cp36-macosx_intel | cp36-manylinux_x86_64 | cp36-manylinux_i686 | cp36-win_amd64 | cp36-win32 |
80+
| Python 3.7 | | cp37-macosx_intel | cp37-manylinux_x86_64 | cp37-manylinux_i686 | cp37-win_amd64 | cp37-win32 |
81+
| Python 3.8 | cp38-macosx_x86_64 | | cp38-manylinux_x86_64 | cp38-manylinux_i686 | cp38-win_amd64 | cp38-win32 |
8282

8383
The list of supported and currently selected build identifiers can also be retrieved by passing the `--print-build-identifiers` flag to `cibuildwheel`.
8484
The format is `python_tag-platform_tag`, with tags similar to those in [PEP 425](https://www.python.org/dev/peps/pep-0425/#details).
@@ -90,10 +90,10 @@ The format is `python_tag-platform_tag`, with tags similar to those in [PEP 425]
9090
CIBW_BUILD: cp36-*
9191
9292
# Skip building on Python 2.7 on the Mac
93-
CIBW_SKIP: cp27-macosx_10_6_intel
93+
CIBW_SKIP: cp27-macosx_intel
9494
9595
# Skip building on Python 3.8 on the Mac
96-
CIBW_SKIP: cp38-macosx_10_9_x86_64
96+
CIBW_SKIP: cp38-macosx_x86_64
9797
9898
# Skip building on Python 2.7 on all platforms
9999
CIBW_SKIP: cp27-*

mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ nav:
1818
- setup.md
1919
- options.md
2020
- deliver-to-pypi.md
21+
- cpp_standards.md
2122
- faq.md
2223
- contributing.md
2324

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import os
2+
import sys
3+
4+
import pytest
5+
6+
import utils
7+
8+
9+
project_dir = os.path.dirname(__file__)
10+
11+
def test_cpp11(tmp_path):
12+
# This test checks that the C++11 standard is supported
13+
14+
add_env = {'CIBW_SKIP': 'cp27-win*', 'CIBW_ENVIRONMENT': 'STANDARD=11'}
15+
# VC++ for Python 2.7 does not support modern standards
16+
if utils.platform == 'macos':
17+
add_env['MACOSX_DEPLOYMENT_TARGET'] = '10.9'
18+
19+
actual_wheels = utils.cibuildwheel_run(project_dir, add_env=add_env)
20+
expected_wheels = [x for x in utils.expected_wheels(
21+
'spam', '0.1.0', macosx_deployment_target='10.9')
22+
if 'cp27-cp27m-win' not in x]
23+
assert set(actual_wheels) == set(expected_wheels)
24+
25+
26+
def test_cpp14():
27+
# This test checks that the C++14 standard is supported
28+
29+
add_env = {'CIBW_SKIP': 'cp27-win* cp35-win*', 'CIBW_ENVIRONMENT': 'STANDARD=14'}
30+
# VC++ for Python 2.7 does not support modern standards
31+
# The manylinux1 docker image does not have a compiler which supports C++11
32+
# Python 3.4 and 3.5 are compiled with MSVC 10, which does not support C++14
33+
if utils.platform == 'macos':
34+
add_env['MACOSX_DEPLOYMENT_TARGET'] = '10.9'
35+
36+
actual_wheels = utils.cibuildwheel_run(project_dir, add_env=add_env)
37+
expected_wheels = [x for x in utils.expected_wheels(
38+
'spam', '0.1.0', macosx_deployment_target='10.9')
39+
if 'cp27-cp27m-win' not in x and 'cp35-cp35m-win' not in x]
40+
assert set(actual_wheels) == set(expected_wheels)
41+
42+
43+
def test_cpp17():
44+
# This test checks that the C++17 standard is supported
45+
46+
# Python 2.7 uses the `register` keyword which is forbidden in the C++17 standard
47+
# The manylinux1 docker image does not have a compiler which supports C++11
48+
# Python 3.4 and 3.5 are compiled with MSVC 10, which does not support C++17
49+
if os.environ.get('APPVEYOR_BUILD_WORKER_IMAGE', '') == 'Visual Studio 2015':
50+
pytest.skip('Visual Studio 2015 does not support C++17')
51+
52+
add_env = {'CIBW_SKIP': 'cp27-win* cp35-win*', 'CIBW_ENVIRONMENT': 'STANDARD=17'}
53+
if utils.platform == 'macos':
54+
add_env['MACOSX_DEPLOYMENT_TARGET'] = '10.13'
55+
56+
actual_wheels = utils.cibuildwheel_run(project_dir, add_env=add_env)
57+
expected_wheels = [x for x in utils.expected_wheels(
58+
'spam', '0.1.0', macosx_deployment_target='10.13')
59+
if 'cp27-cp27m-win' not in x and 'cp35-cp35m-win' not in x]
60+
assert set(actual_wheels) == set(expected_wheels)

test/10_cpp_standards/setup.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import os, sys
2+
from setuptools import setup, Extension
3+
import platform
4+
5+
standard = os.environ["STANDARD"]
6+
7+
language_standard = "/std:c++" + standard if platform.system() == "Windows" else "-std=c++" + standard
8+
9+
extra_compile_args = [language_standard, "-DSTANDARD=" + standard]
10+
11+
if standard == "17":
12+
if platform.system() == "Windows":
13+
extra_compile_args.append("/wd5033")
14+
else:
15+
extra_compile_args.append("-Wno-register")
16+
17+
setup(
18+
name="spam",
19+
ext_modules=[Extension('spam', sources=['spam.cpp'], language="c++", extra_compile_args=extra_compile_args)],
20+
version="0.1.0",
21+
)

test/10_cpp_standards/spam.cpp

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#include <Python.h>
2+
#include <string>
3+
4+
// Depending on the requested standard, use a modern C++ feature
5+
// that was introduced in that standard.
6+
#if STANDARD == 11
7+
#include <array>
8+
#elif STANDARD == 14
9+
int a = 100'000;
10+
#elif STANDARD == 17
11+
#include <utility>
12+
auto a = std::pair(5.0, false);
13+
#else
14+
#error Standard needed
15+
#endif
16+
17+
static PyObject *
18+
spam_system(PyObject *self, PyObject *args)
19+
{
20+
const char *command;
21+
int sts;
22+
23+
if (!PyArg_ParseTuple(args, "s", &command))
24+
return NULL;
25+
sts = system(command);
26+
return PyLong_FromLong(sts);
27+
}
28+
29+
/* Module initialization */
30+
31+
#if PY_MAJOR_VERSION >= 3
32+
#define MOD_INIT(name) PyMODINIT_FUNC PyInit_##name(void)
33+
#define MOD_DEF(m, name, doc, methods, module_state_size) \
34+
static struct PyModuleDef moduledef = { \
35+
PyModuleDef_HEAD_INIT, name, doc, module_state_size, methods, }; \
36+
m = PyModule_Create(&moduledef);
37+
#define MOD_RETURN(m) return m;
38+
#else
39+
#define MOD_INIT(name) PyMODINIT_FUNC init##name(void)
40+
#define MOD_DEF(m, name, doc, methods, module_state_size) \
41+
m = Py_InitModule3(name, methods, doc);
42+
#define MOD_RETURN(m) return;
43+
#endif
44+
45+
static PyMethodDef module_methods[] = {
46+
{"system", (PyCFunction)spam_system, METH_VARARGS,
47+
"Execute a shell command."},
48+
{NULL} /* Sentinel */
49+
};
50+
51+
MOD_INIT(spam)
52+
{
53+
PyObject* m;
54+
55+
MOD_DEF(m,
56+
"spam",
57+
"Example module",
58+
module_methods,
59+
-1)
60+
61+
MOD_RETURN(m)
62+
}

test/shared/utils.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ def cibuildwheel_run(project_path, env=None, add_env=None, output_dir=None):
5353
'''
5454
if env is None:
5555
env = os.environ.copy()
56+
# If present in the host environment, remove the MACOSX_DEPLOYMENT_TARGET for consistency
57+
env.pop('MACOSX_DEPLOYMENT_TARGET', None)
5658

5759
if add_env is not None:
5860
env.update(add_env)
@@ -66,7 +68,8 @@ def cibuildwheel_run(project_path, env=None, add_env=None, output_dir=None):
6668
return wheels
6769

6870

69-
def expected_wheels(package_name, package_version, manylinux_versions=['manylinux1', 'manylinux2010']):
71+
def expected_wheels(package_name, package_version, manylinux_versions=['manylinux1', 'manylinux2010'],
72+
macosx_deployment_target=None):
7073
'''
7174
Returns a list of expected wheels from a run of cibuildwheel.
7275
'''
@@ -83,17 +86,22 @@ def expected_wheels(package_name, package_version, manylinux_versions=['manylinu
8386
platform_tags.append('{manylinux_version}_{architecture}'.format(
8487
manylinux_version=manylinux_version, architecture=architecture
8588
))
89+
8690
def get_platform_tags(python_abi_tag):
8791
return platform_tags
92+
8893
elif platform == 'windows':
94+
8995
def get_platform_tags(python_abi_tag):
9096
return ['win32', 'win_amd64']
97+
9198
elif platform == 'macos':
99+
92100
def get_platform_tags(python_abi_tag):
93101
if python_abi_tag == 'cp38-cp38':
94-
return ['macosx_10_9_x86_64']
102+
return ['macosx_' + (macosx_deployment_target or "10.9").replace(".", "_") + '_x86_64']
95103
else:
96-
return ['macosx_10_6_intel']
104+
return ['macosx_' + (macosx_deployment_target or "10.6").replace(".", "_") + '_intel']
97105
else:
98106
raise Exception('unsupported platform')
99107

0 commit comments

Comments
 (0)