From 758f158bbdba4759851ec768a6f3bd4e91e20770 Mon Sep 17 00:00:00 2001 From: Grzegorz Bokota Date: Sun, 1 Dec 2019 23:02:04 +0100 Subject: [PATCH 1/8] Update information about need to use modern standards in c++ add test for c++ standards for all platform change macos platform tag on more generic. --- cibuildwheel/__main__.py | 3 ++ cibuildwheel/macos.py | 14 ++--- test/10_cpp_standards/cibuildwheel_test.py | 55 +++++++++++++++++++ test/10_cpp_standards/setup.py | 21 ++++++++ test/10_cpp_standards/spam.cpp | 62 ++++++++++++++++++++++ 5 files changed, 149 insertions(+), 6 deletions(-) create mode 100644 test/10_cpp_standards/cibuildwheel_test.py create mode 100644 test/10_cpp_standards/setup.py create mode 100644 test/10_cpp_standards/spam.cpp diff --git a/cibuildwheel/__main__.py b/cibuildwheel/__main__.py index ce9a72fc..9083707f 100644 --- a/cibuildwheel/__main__.py +++ b/cibuildwheel/__main__.py @@ -203,6 +203,9 @@ def detect_obsolete_options(): if deprecated in os.environ and 'manylinux1' in os.environ[deprecated]: print("Build identifiers with 'manylinux1' been deprecated. Replacing all occurences of 'manylinux1' by 'manylinux' in the option '{}'".format(deprecated)) os.environ[deprecated] = os.environ[deprecated].replace('manylinux1', 'manylinux') + if deprecated in os.environ and ("macosx_10_6" in os.environ[deprecated] or "macosx_10_9" in os.environ[deprecated]): + print("Build identifiers with 'macosx_10_6' or 'macosx_10_9' been deprecated. Replacing with 'macosx' in the option '{}'".format(deprecated)) + os.environ[deprecated] = os.environ[deprecated].replace('macosx_10_6', 'macosx').replace('macosx_10_9', 'macosx') def print_preamble(platform, build_options): diff --git a/cibuildwheel/macos.py b/cibuildwheel/macos.py index e8dfb775..d7cde8bf 100644 --- a/cibuildwheel/macos.py +++ b/cibuildwheel/macos.py @@ -14,11 +14,11 @@ from .util import prepare_command, get_build_verbosity_extra_flags def get_python_configurations(build_selector): PythonConfiguration = namedtuple('PythonConfiguration', ['version', 'identifier', 'url']) python_configurations = [ - 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'), - 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'), - 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'), - 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'), - 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'), + 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'), + 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'), + 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'), + 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'), + 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') ] # skip builds as required @@ -96,7 +96,9 @@ def build(project_dir, output_dir, test_command, test_requires, test_extras, bef call(['python', get_pip_script, '--no-setuptools', '--no-wheel'], env=env, cwd="/tmp") assert os.path.exists(os.path.join(installation_bin_path, 'pip')) call(['pip', '--version'], env=env) - call(['pip', 'install', '--upgrade', 'setuptools', 'wheel', 'delocate'], env=env) + call(['pip', 'install', '--upgrade', 'setuptools'], env=env) + call(['pip', 'install', 'git+https://github.com/Czaki/wheel.git@1a907d36eb4921efa0c9e7dce6b570aa5f84d7f5'], env=env) + call(['pip', 'install', 'delocate'], env=env) # run the before_build command if before_build: diff --git a/test/10_cpp_standards/cibuildwheel_test.py b/test/10_cpp_standards/cibuildwheel_test.py new file mode 100644 index 00000000..04734c8b --- /dev/null +++ b/test/10_cpp_standards/cibuildwheel_test.py @@ -0,0 +1,55 @@ +import os +import sys + +import pytest + +import utils + + +def test_cpp11(tmp_path): + add_env = {"CIBW_SKIP": "cp27-win*", "CIBW_ENVIRONMENT": "STANDARD=11"} + # VC for python 2.7 do not support modern standards + if utils.platform == "macos": + add_env["MACOSX_DEPLOYMENT_TARGET"] = "10.9" + project_dir = os.path.dirname(__file__) + # this test checks if c++11 standard is supported. + + actual_wheels = utils.cibuildwheel_run(project_dir, add_env=add_env) + expected_wheels = [x for x in utils.expected_wheels('spam', '0.1.0', "10.9") if "cp27-cp27m-win" not in x] + assert set(actual_wheels) == set(expected_wheels) + + +def test_cpp14(): + add_env = {"CIBW_SKIP": "cp27-win* cp35-win*", "CIBW_ENVIRONMENT": "STANDARD=14"} + # VC for python 2.7 do not support modern standards + # manylinux1 docker image do not support compilers with standards newer than c++11 + # python 3.4 and 3.5 are compiled with MSVC 10. which not support c++14 + if utils.platform == "macos": + add_env["MACOSX_DEPLOYMENT_TARGET"] = "10.9" + project_dir = os.path.dirname(__file__) + # this test checks if c++14 standard is supported. + + actual_wheels = utils.cibuildwheel_run(project_dir, add_env=add_env) + expected_wheels = [x for x in utils.expected_wheels('spam', '0.1.0', "10.9") + if "cp27-cp27m-win" not in x and "cp35-cp35m-win" not in x] + assert set(actual_wheels) == set(expected_wheels) + +def test_cpp17(): + # python 2.7 use `register` keyword which is forbidden in c++17 standard + # manylinux1 docker image do not support compilers with standards newer than c++11 + # python 3.4 and 3.5 are compiled with MSVC 10. which not support c++17 + if os.environ.get("APPVEYOR_BUILD_WORKER_IMAGE", "") == "Visual Studio 2015": + pytest.skip("Visual Studio 2015 does not support c++17") + + add_env = {"CIBW_SKIP": "cp27-win* cp35-win*", "CIBW_ENVIRONMENT": "STANDARD=17"} + if utils.platform == "macos": + add_env["MACOSX_DEPLOYMENT_TARGET"] = "10.13" + + project_dir = os.path.dirname(__file__) + # this test checks if c++17 standard is supported. + + actual_wheels = utils.cibuildwheel_run(project_dir, add_env=add_env) + expected_wheels = [x for x in utils.expected_wheels('spam', '0.1.0', "10.13") + if "cp27-cp27m-win" not in x and "cp35-cp35m-win" not in x] + assert set(actual_wheels) == set(expected_wheels) + diff --git a/test/10_cpp_standards/setup.py b/test/10_cpp_standards/setup.py new file mode 100644 index 00000000..85e9492c --- /dev/null +++ b/test/10_cpp_standards/setup.py @@ -0,0 +1,21 @@ +import os, sys +from setuptools import setup, Extension +import platform + +standard = os.environ["STANDARD"] + +language_standard = "/std:c++" + standard if platform.system() == "Windows" else "-std=c++" + standard + +extra_compile_args=[language_standard, "-DSTANDARD=" + standard] + +if standard == "17": + if platform.system() == "Windows": + extra_compile_args.append("/wd5033") + else: + extra_compile_args.append("-Wno-register") + +setup( + name="spam", + ext_modules=[Extension('spam', sources=['spam.cpp'], language="c++", extra_compile_args=extra_compile_args)], + version="0.1.0", +) diff --git a/test/10_cpp_standards/spam.cpp b/test/10_cpp_standards/spam.cpp new file mode 100644 index 00000000..d619c54b --- /dev/null +++ b/test/10_cpp_standards/spam.cpp @@ -0,0 +1,62 @@ +#include +#include +#if STANDARD == 11 +#include +#elif STANDARD == 14 +int a = 100'000; +#elif STANDARD == 17 +#include +auto a = std::pair(5.0, false); +#else +#error Standard needed +#endif + +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) + +static PyObject * +spam_system(PyObject *self, PyObject *args) +{ + const char *command; + int sts; + + if (!PyArg_ParseTuple(args, "s", &command)) + return NULL; + sts = system(command); + return PyLong_FromLong(sts); +} + +/* Module initialization */ + +#if PY_MAJOR_VERSION >= 3 + #define MOD_INIT(name) PyMODINIT_FUNC PyInit_##name(void) + #define MOD_DEF(m, name, doc, methods, module_state_size) \ + static struct PyModuleDef moduledef = { \ + PyModuleDef_HEAD_INIT, name, doc, module_state_size, methods, }; \ + m = PyModule_Create(&moduledef); + #define MOD_RETURN(m) return m; +#else + #define MOD_INIT(name) PyMODINIT_FUNC init##name(void) + #define MOD_DEF(m, name, doc, methods, module_state_size) \ + m = Py_InitModule3(name, methods, doc); + #define MOD_RETURN(m) return; +#endif + +static PyMethodDef module_methods[] = { + {"system", (PyCFunction)spam_system, METH_VARARGS, + "Execute a shell command."}, + {NULL} /* Sentinel */ +}; + +MOD_INIT(spam) +{ + PyObject* m; + + MOD_DEF(m, + "spam", + "Example module", + module_methods, + -1) + + MOD_RETURN(m) +} From 9cb796edcd5bc46328cf1c16d30018873fd562d9 Mon Sep 17 00:00:00 2001 From: Grzegorz Bokota Date: Sun, 1 Dec 2019 23:54:45 +0100 Subject: [PATCH 2/8] update docs --- docs/cpp_standards.md | 31 +++++++++++++++++++++++++++++++ docs/options.md | 18 +++++++++--------- mkdocs.yml | 1 + 3 files changed, 41 insertions(+), 9 deletions(-) create mode 100644 docs/cpp_standards.md diff --git a/docs/cpp_standards.md b/docs/cpp_standards.md new file mode 100644 index 00000000..aac299cb --- /dev/null +++ b/docs/cpp_standards.md @@ -0,0 +1,31 @@ +--- +title: Modern C++ standards +--- + +Building Python wheels with modern C++ (C++11) requires a few tricks. + +Creating wheel for python 2.7 which need c++17 needs special configuration because python 2.7 header files using `register` keyword. +In c++17 this keyword is reserved ((see)[https://en.cppreference.com/w/cpp/keyword/register]) +It is possible to allow usage of `register` using proper flag `-Wno-register` for gcc/clang and `/wd5033` for MSVC. + +## Linux +When using default `manylinux1` image it is possible to use only c++11 and earlier standards. +This is about how you can make C++14 wheels in `manylinux1`, through some tricks https://github.com/pypa/manylinux/issues/118 + +`manylinux2010` supports all C++ standards (up to c++17). + +## MacOS + +To get C++11 and C++14 support, set `MACOSX_DEPLOYMENT_TARGET` variable to `"10.9"`. + +To get C++17 support, set `MACOSX_DEPLOYMENT_TARGET` variable to `"10.13"` or `"10.14"`. (`"10.13"` supports C++17 partially, e.g. the filesystem header is in experimental: `#include ` -> `#include `) + +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) + +## Windows + +Visual C++ for Python 2.7 does not support modern standards of C++. +When building on Appveyor, you'll need to use either the 'Visual Studio 2017' or 'Visual Studio 2019' image. Note that Python 2.7 isn't supported on these images - you should skip it using `CIBW_SKIP=cp27-win*`. + +There is option for workaround this limitation. PyBind project has in documentation example how to compile python 2.7 extension with newer compiler https://github.com/pybind/python_example \ No newline at end of file diff --git a/docs/options.md b/docs/options.md index da407032..df7d1a2a 100644 --- a/docs/options.md +++ b/docs/options.md @@ -64,7 +64,7 @@ This option can also be set using the command-line option `--platform`. > Choose the Python versions to build -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. +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. 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. @@ -72,13 +72,13 @@ When setting the options, you can use shell-style globbing syntax (as per `fnmat
-| | macOS 64bit | macOS 32/64bit | Manylinux 64bit | Manylinux 32bit | Windows 64bit | Windows 32bit | -|------------|-------------------------|------------------------|------------------------|----------------------|-----------------|----------------| -| Python 2.7 | | cp27-macosx_10_6_intel | cp27-manylinux_x86_64 | cp27-manylinux_i686 | cp27-win_amd64 | cp27-win32 | -| Python 3.5 | | cp35-macosx_10_6_intel | cp35-manylinux_x86_64 | cp35-manylinux_i686 | cp35-win_amd64 | cp35-win32 | -| Python 3.6 | | cp36-macosx_10_6_intel | cp36-manylinux_x86_64 | cp36-manylinux_i686 | cp36-win_amd64 | cp36-win32 | -| Python 3.7 | | cp37-macosx_10_6_intel | cp37-manylinux_x86_64 | cp37-manylinux_i686 | cp37-win_amd64 | cp37-win32 | -| Python 3.8 | cp38-macosx_10_9_x86_64 | | cp38-manylinux_x86_64 | cp38-manylinux_i686 | cp38-win_amd64 | cp38-win32 | +| | macOS 64bit | macOS 32/64bit | Manylinux 64bit | Manylinux 32bit | Windows 64bit | Windows 32bit | +|------------|--------------------|-------------------|------------------------|----------------------|-----------------|----------------| +| Python 2.7 | | cp27-macosx_intel | cp27-manylinux_x86_64 | cp27-manylinux_i686 | cp27-win_amd64 | cp27-win32 | +| Python 3.5 | | cp35-macosx_intel | cp35-manylinux_x86_64 | cp35-manylinux_i686 | cp35-win_amd64 | cp35-win32 | +| Python 3.6 | | cp36-macosx_intel | cp36-manylinux_x86_64 | cp36-manylinux_i686 | cp36-win_amd64 | cp36-win32 | +| Python 3.7 | | cp37-macosx_intel | cp37-manylinux_x86_64 | cp37-manylinux_i686 | cp37-win_amd64 | cp37-win32 | +| Python 3.8 | cp38-macosx_x86_64 | | cp38-manylinux_x86_64 | cp38-manylinux_i686 | cp38-win_amd64 | cp38-win32 | The list of supported and currently selected build identifiers can also be retrieved by passing the `--print-build-identifiers` flag to `cibuildwheel`. 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,7 +90,7 @@ The format is `python_tag-platform_tag`, with tags similar to those in [PEP 425] CIBW_BUILD: cp36-* # Skip building on Python 2.7 on the Mac -CIBW_SKIP: cp27-macosx_10_6_intel +CIBW_SKIP: cp27-macosx_intel # Skip building on Python 3.8 on the Mac CIBW_SKIP: cp38-macosx_10_9_x86_64 diff --git a/mkdocs.yml b/mkdocs.yml index 43e5be2d..5c12f501 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -18,6 +18,7 @@ nav: - setup.md - options.md - deliver-to-pypi.md + - cpp_standards.md - faq.md - contributing.md From e64916fe9cccba9e63e05826e2ca48a4296d217f Mon Sep 17 00:00:00 2001 From: Yannick Jadoul Date: Mon, 9 Dec 2019 00:39:29 +0100 Subject: [PATCH 3/8] Rewording a few things in cpp_standards.md, and some other minor fixes --- cibuildwheel/__main__.py | 4 ++-- cibuildwheel/macos.py | 2 +- docs/cpp_standards.md | 36 +++++++++++++++++++----------------- docs/options.md | 16 ++++++++-------- 4 files changed, 30 insertions(+), 28 deletions(-) diff --git a/cibuildwheel/__main__.py b/cibuildwheel/__main__.py index 9083707f..9b3db905 100644 --- a/cibuildwheel/__main__.py +++ b/cibuildwheel/__main__.py @@ -201,10 +201,10 @@ def detect_obsolete_options(): # Check for 'manylinux1' in the 'CIBW_BUILD' and 'CIBW_SKIP' options for deprecated in ['CIBW_BUILD', 'CIBW_SKIP']: if deprecated in os.environ and 'manylinux1' in os.environ[deprecated]: - print("Build identifiers with 'manylinux1' been deprecated. Replacing all occurences of 'manylinux1' by 'manylinux' in the option '{}'".format(deprecated)) + print("Build identifiers with 'manylinux1' have been deprecated. Replacing all occurences of 'manylinux1' by 'manylinux' in the option '{}'".format(deprecated)) os.environ[deprecated] = os.environ[deprecated].replace('manylinux1', 'manylinux') if deprecated in os.environ and ("macosx_10_6" in os.environ[deprecated] or "macosx_10_9" in os.environ[deprecated]): - print("Build identifiers with 'macosx_10_6' or 'macosx_10_9' been deprecated. Replacing with 'macosx' in the option '{}'".format(deprecated)) + print("Build identifiers with 'macosx_10_6' or 'macosx_10_9' have been deprecated. Replacing all occurences with 'macosx' in the option '{}'".format(deprecated)) os.environ[deprecated] = os.environ[deprecated].replace('macosx_10_6', 'macosx').replace('macosx_10_9', 'macosx') diff --git a/cibuildwheel/macos.py b/cibuildwheel/macos.py index d7cde8bf..361e8d2e 100644 --- a/cibuildwheel/macos.py +++ b/cibuildwheel/macos.py @@ -18,7 +18,7 @@ def get_python_configurations(build_selector): 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'), 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'), 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'), - 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') + 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'), ] # skip builds as required diff --git a/docs/cpp_standards.md b/docs/cpp_standards.md index aac299cb..d330da48 100644 --- a/docs/cpp_standards.md +++ b/docs/cpp_standards.md @@ -2,30 +2,32 @@ title: Modern C++ standards --- -Building Python wheels with modern C++ (C++11) requires a few tricks. +Building Python wheels with modern C++ standards (C++11 and later) requires a few tricks. -Creating wheel for python 2.7 which need c++17 needs special configuration because python 2.7 header files using `register` keyword. -In c++17 this keyword is reserved ((see)[https://en.cppreference.com/w/cpp/keyword/register]) -It is possible to allow usage of `register` using proper flag `-Wno-register` for gcc/clang and `/wd5033` for MSVC. -## Linux -When using default `manylinux1` image it is possible to use only c++11 and earlier standards. -This is about how you can make C++14 wheels in `manylinux1`, through some tricks https://github.com/pypa/manylinux/issues/118 +## Python 2.7 and C++17 -`manylinux2010` supports all C++ standards (up to c++17). +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. -## MacOS +## manylinux1 and C++14 +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 -To get C++11 and C++14 support, set `MACOSX_DEPLOYMENT_TARGET` variable to `"10.9"`. +`manylinux2010` and `manylinux2014` are newer and support all C++ standards (up to C++17). -To get C++17 support, set `MACOSX_DEPLOYMENT_TARGET` variable to `"10.13"` or `"10.14"`. (`"10.13"` supports C++17 partially, e.g. the filesystem header is in experimental: `#include ` -> `#include `) +## macOS and deployment target versions -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) +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. -## Windows +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). -Visual C++ for Python 2.7 does not support modern standards of C++. -When building on Appveyor, you'll need to use either the 'Visual Studio 2017' or 'Visual Studio 2019' image. Note that Python 2.7 isn't supported on these images - you should skip it using `CIBW_SKIP=cp27-win*`. +To get C++11 and C++14 support, set `MACOSX_DEPLOYMENT_TARGET` to (at least) `"10.9"`. -There is option for workaround this limitation. PyBind project has in documentation example how to compile python 2.7 extension with newer compiler https://github.com/pybind/python_example \ No newline at end of file +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 ` instead of `#include `; macOS 10.14 has full C++17 support). + +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. + +## Windows and Python 2.7 + +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*`. + +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. diff --git a/docs/options.md b/docs/options.md index df7d1a2a..52433586 100644 --- a/docs/options.md +++ b/docs/options.md @@ -72,13 +72,13 @@ When setting the options, you can use shell-style globbing syntax (as per `fnmat
-| | macOS 64bit | macOS 32/64bit | Manylinux 64bit | Manylinux 32bit | Windows 64bit | Windows 32bit | -|------------|--------------------|-------------------|------------------------|----------------------|-----------------|----------------| -| Python 2.7 | | cp27-macosx_intel | cp27-manylinux_x86_64 | cp27-manylinux_i686 | cp27-win_amd64 | cp27-win32 | -| Python 3.5 | | cp35-macosx_intel | cp35-manylinux_x86_64 | cp35-manylinux_i686 | cp35-win_amd64 | cp35-win32 | -| Python 3.6 | | cp36-macosx_intel | cp36-manylinux_x86_64 | cp36-manylinux_i686 | cp36-win_amd64 | cp36-win32 | -| Python 3.7 | | cp37-macosx_intel | cp37-manylinux_x86_64 | cp37-manylinux_i686 | cp37-win_amd64 | cp37-win32 | -| Python 3.8 | cp38-macosx_x86_64 | | cp38-manylinux_x86_64 | cp38-manylinux_i686 | cp38-win_amd64 | cp38-win32 | +| | macOS 64bit | macOS 32/64bit | Manylinux 64bit | Manylinux 32bit | Windows 64bit | Windows 32bit | +|------------|---------------------|--------------------|------------------------|----------------------|-----------------|----------------| +| Python 2.7 | | cp27-macosx_intel | cp27-manylinux_x86_64 | cp27-manylinux_i686 | cp27-win_amd64 | cp27-win32 | +| Python 3.5 | | cp35-macosx_intel | cp35-manylinux_x86_64 | cp35-manylinux_i686 | cp35-win_amd64 | cp35-win32 | +| Python 3.6 | | cp36-macosx_intel | cp36-manylinux_x86_64 | cp36-manylinux_i686 | cp36-win_amd64 | cp36-win32 | +| Python 3.7 | | cp37-macosx_intel | cp37-manylinux_x86_64 | cp37-manylinux_i686 | cp37-win_amd64 | cp37-win32 | +| Python 3.8 | cp38-macosx_x86_64 | | cp38-manylinux_x86_64 | cp38-manylinux_i686 | cp38-win_amd64 | cp38-win32 | The list of supported and currently selected build identifiers can also be retrieved by passing the `--print-build-identifiers` flag to `cibuildwheel`. The format is `python_tag-platform_tag`, with tags similar to those in [PEP 425](https://www.python.org/dev/peps/pep-0425/#details). @@ -93,7 +93,7 @@ CIBW_BUILD: cp36-* CIBW_SKIP: cp27-macosx_intel # Skip building on Python 3.8 on the Mac -CIBW_SKIP: cp38-macosx_10_9_x86_64 +CIBW_SKIP: cp38-macosx_x86_64 # Skip building on Python 2.7 on all platforms CIBW_SKIP: cp27-* From 1f784a1a29a79470f93c062139ea6080a1d1f831 Mon Sep 17 00:00:00 2001 From: Grzegorz Bokota Date: Mon, 9 Dec 2019 09:03:29 +0100 Subject: [PATCH 4/8] fix conflicts with main branch --- test/10_cpp_standards/cibuildwheel_test.py | 9 ++++++--- test/shared/utils.py | 19 +++++++++++++++---- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/test/10_cpp_standards/cibuildwheel_test.py b/test/10_cpp_standards/cibuildwheel_test.py index 04734c8b..ef53c4ad 100644 --- a/test/10_cpp_standards/cibuildwheel_test.py +++ b/test/10_cpp_standards/cibuildwheel_test.py @@ -15,7 +15,8 @@ def test_cpp11(tmp_path): # this test checks if c++11 standard is supported. actual_wheels = utils.cibuildwheel_run(project_dir, add_env=add_env) - expected_wheels = [x for x in utils.expected_wheels('spam', '0.1.0', "10.9") if "cp27-cp27m-win" not in x] + expected_wheels = [x for x in utils.expected_wheels('spam', '0.1.0', + macosx_deployment_target="10.9") if "cp27-cp27m-win" not in x] assert set(actual_wheels) == set(expected_wheels) @@ -30,7 +31,8 @@ def test_cpp14(): # this test checks if c++14 standard is supported. actual_wheels = utils.cibuildwheel_run(project_dir, add_env=add_env) - expected_wheels = [x for x in utils.expected_wheels('spam', '0.1.0', "10.9") + expected_wheels = [x for x in utils.expected_wheels( + 'spam', '0.1.0', macosx_deployment_target="10.9") if "cp27-cp27m-win" not in x and "cp35-cp35m-win" not in x] assert set(actual_wheels) == set(expected_wheels) @@ -49,7 +51,8 @@ def test_cpp17(): # this test checks if c++17 standard is supported. actual_wheels = utils.cibuildwheel_run(project_dir, add_env=add_env) - expected_wheels = [x for x in utils.expected_wheels('spam', '0.1.0', "10.13") + expected_wheels = [x for x in utils.expected_wheels('spam', '0.1.0', + macosx_deployment_target="10.13") if "cp27-cp27m-win" not in x and "cp35-cp35m-win" not in x] assert set(actual_wheels) == set(expected_wheels) diff --git a/test/shared/utils.py b/test/shared/utils.py index 4939b64e..3615a3db 100644 --- a/test/shared/utils.py +++ b/test/shared/utils.py @@ -66,7 +66,8 @@ def cibuildwheel_run(project_path, env=None, add_env=None, output_dir=None): return wheels -def expected_wheels(package_name, package_version, manylinux_versions=['manylinux1', 'manylinux2010']): +def expected_wheels(package_name, package_version, manylinux_versions=['manylinux1', 'manylinux2010'], + macosx_deployment_target=None): ''' Returns a list of expected wheels from a run of cibuildwheel. ''' @@ -83,20 +84,30 @@ def expected_wheels(package_name, package_version, manylinux_versions=['manylinu platform_tags.append('{manylinux_version}_{architecture}'.format( manylinux_version=manylinux_version, architecture=architecture )) + def get_platform_tags(python_abi_tag): return platform_tags + elif platform == 'windows': + def get_platform_tags(python_abi_tag): return ['win32', 'win_amd64'] + elif platform == 'macos': + if macosx_deployment_target is not None: + tag = macosx_deployment_target.replace(".", "_") + tag1 = macosx_deployment_target.replace(".", "_") + else: + tag = os.environ.get("MACOSX_DEPLOYMENT_TARGET", "10_6").replace(".", "_") + tag1 = os.environ.get("MACOSX_DEPLOYMENT_TARGET", "10_9").replace(".", "_") + def get_platform_tags(python_abi_tag): if python_abi_tag == 'cp38-cp38': - return ['macosx_10_9_x86_64'] + return ['macosx_' + tag1 + '_x86_64'] else: - return ['macosx_10_6_intel'] + return ['macosx_' + tag + '_intel'] else: raise Exception('unsupported platform') - templates = [] for python_abi_tag in python_abi_tags: for platform_tag in get_platform_tags(python_abi_tag): From 0d5a2a78431480ab3815dedeb396e176861d4c6d Mon Sep 17 00:00:00 2001 From: Yannick Jadoul Date: Mon, 9 Dec 2019 11:58:50 +0100 Subject: [PATCH 5/8] Moving MACOSX_DEPLOYMENT_TARGET from test utils' expected_wheels to run_cibuildwheel, and some minor style fixes --- test/10_cpp_standards/cibuildwheel_test.py | 66 +++++++++++----------- test/10_cpp_standards/setup.py | 2 +- test/10_cpp_standards/spam.cpp | 24 ++++---- test/shared/utils.py | 13 ++--- 4 files changed, 52 insertions(+), 53 deletions(-) diff --git a/test/10_cpp_standards/cibuildwheel_test.py b/test/10_cpp_standards/cibuildwheel_test.py index ef53c4ad..2fc172e0 100644 --- a/test/10_cpp_standards/cibuildwheel_test.py +++ b/test/10_cpp_standards/cibuildwheel_test.py @@ -6,53 +6,55 @@ import pytest import utils +project_dir = os.path.dirname(__file__) + def test_cpp11(tmp_path): - add_env = {"CIBW_SKIP": "cp27-win*", "CIBW_ENVIRONMENT": "STANDARD=11"} - # VC for python 2.7 do not support modern standards - if utils.platform == "macos": - add_env["MACOSX_DEPLOYMENT_TARGET"] = "10.9" - project_dir = os.path.dirname(__file__) - # this test checks if c++11 standard is supported. + # This test checks that the C++11 standard is supported + + add_env = {'CIBW_SKIP': 'cp27-win*', 'CIBW_ENVIRONMENT': 'STANDARD=11'} + # VC++ for Python 2.7 does not support modern standards + if utils.platform == 'macos': + add_env['MACOSX_DEPLOYMENT_TARGET'] = '10.9' actual_wheels = utils.cibuildwheel_run(project_dir, add_env=add_env) - expected_wheels = [x for x in utils.expected_wheels('spam', '0.1.0', - macosx_deployment_target="10.9") if "cp27-cp27m-win" not in x] + expected_wheels = [x for x in utils.expected_wheels( + 'spam', '0.1.0', macosx_deployment_target='10.9') + if 'cp27-cp27m-win' not in x] assert set(actual_wheels) == set(expected_wheels) def test_cpp14(): - add_env = {"CIBW_SKIP": "cp27-win* cp35-win*", "CIBW_ENVIRONMENT": "STANDARD=14"} - # VC for python 2.7 do not support modern standards - # manylinux1 docker image do not support compilers with standards newer than c++11 - # python 3.4 and 3.5 are compiled with MSVC 10. which not support c++14 - if utils.platform == "macos": - add_env["MACOSX_DEPLOYMENT_TARGET"] = "10.9" - project_dir = os.path.dirname(__file__) - # this test checks if c++14 standard is supported. + # This test checks that the C++14 standard is supported + + add_env = {'CIBW_SKIP': 'cp27-win* cp35-win*', 'CIBW_ENVIRONMENT': 'STANDARD=14'} + # VC++ for Python 2.7 does not support modern standards + # The manylinux1 docker image does not have a compiler which supports C++11 + # Python 3.4 and 3.5 are compiled with MSVC 10, which does not support C++14 + if utils.platform == 'macos': + add_env['MACOSX_DEPLOYMENT_TARGET'] = '10.9' actual_wheels = utils.cibuildwheel_run(project_dir, add_env=add_env) expected_wheels = [x for x in utils.expected_wheels( - 'spam', '0.1.0', macosx_deployment_target="10.9") - if "cp27-cp27m-win" not in x and "cp35-cp35m-win" not in x] + 'spam', '0.1.0', macosx_deployment_target='10.9') + if 'cp27-cp27m-win' not in x and 'cp35-cp35m-win' not in x] assert set(actual_wheels) == set(expected_wheels) + def test_cpp17(): - # python 2.7 use `register` keyword which is forbidden in c++17 standard - # manylinux1 docker image do not support compilers with standards newer than c++11 - # python 3.4 and 3.5 are compiled with MSVC 10. which not support c++17 - if os.environ.get("APPVEYOR_BUILD_WORKER_IMAGE", "") == "Visual Studio 2015": - pytest.skip("Visual Studio 2015 does not support c++17") + # This test checks that the C++17 standard is supported - add_env = {"CIBW_SKIP": "cp27-win* cp35-win*", "CIBW_ENVIRONMENT": "STANDARD=17"} - if utils.platform == "macos": - add_env["MACOSX_DEPLOYMENT_TARGET"] = "10.13" + # Python 2.7 uses the `register` keyword which is forbidden in the C++17 standard + # The manylinux1 docker image does not have a compiler which supports C++11 + # Python 3.4 and 3.5 are compiled with MSVC 10, which does not support C++17 + if os.environ.get('APPVEYOR_BUILD_WORKER_IMAGE', '') == 'Visual Studio 2015': + pytest.skip('Visual Studio 2015 does not support C++17') - project_dir = os.path.dirname(__file__) - # this test checks if c++17 standard is supported. + add_env = {'CIBW_SKIP': 'cp27-win* cp35-win*', 'CIBW_ENVIRONMENT': 'STANDARD=17'} + if utils.platform == 'macos': + add_env['MACOSX_DEPLOYMENT_TARGET'] = '10.13' actual_wheels = utils.cibuildwheel_run(project_dir, add_env=add_env) - expected_wheels = [x for x in utils.expected_wheels('spam', '0.1.0', - macosx_deployment_target="10.13") - if "cp27-cp27m-win" not in x and "cp35-cp35m-win" not in x] + expected_wheels = [x for x in utils.expected_wheels( + 'spam', '0.1.0', macosx_deployment_target='10.13') + if 'cp27-cp27m-win' not in x and 'cp35-cp35m-win' not in x] assert set(actual_wheels) == set(expected_wheels) - diff --git a/test/10_cpp_standards/setup.py b/test/10_cpp_standards/setup.py index 85e9492c..969ca39b 100644 --- a/test/10_cpp_standards/setup.py +++ b/test/10_cpp_standards/setup.py @@ -6,7 +6,7 @@ standard = os.environ["STANDARD"] language_standard = "/std:c++" + standard if platform.system() == "Windows" else "-std=c++" + standard -extra_compile_args=[language_standard, "-DSTANDARD=" + standard] +extra_compile_args = [language_standard, "-DSTANDARD=" + standard] if standard == "17": if platform.system() == "Windows": diff --git a/test/10_cpp_standards/spam.cpp b/test/10_cpp_standards/spam.cpp index d619c54b..f9b56d07 100644 --- a/test/10_cpp_standards/spam.cpp +++ b/test/10_cpp_standards/spam.cpp @@ -1,18 +1,18 @@ #include #include -#if STANDARD == 11 -#include -#elif STANDARD == 14 -int a = 100'000; -#elif STANDARD == 17 -#include -auto a = std::pair(5.0, false); -#else -#error Standard needed -#endif -#define STR_HELPER(x) #x -#define STR(x) STR_HELPER(x) +// Depending on the requested standard, use a modern C++ feature +// that was introduced in that standard. +#if STANDARD == 11 + #include +#elif STANDARD == 14 + int a = 100'000; +#elif STANDARD == 17 + #include + auto a = std::pair(5.0, false); +#else + #error Standard needed +#endif static PyObject * spam_system(PyObject *self, PyObject *args) diff --git a/test/shared/utils.py b/test/shared/utils.py index 3615a3db..9ed730ff 100644 --- a/test/shared/utils.py +++ b/test/shared/utils.py @@ -53,6 +53,8 @@ def cibuildwheel_run(project_path, env=None, add_env=None, output_dir=None): ''' if env is None: env = os.environ.copy() + # If present in the host environment, remove the MACOSX_DEPLOYMENT_TARGET for consistency + env.pop('MACOSX_DEPLOYMENT_TARGET', None) if add_env is not None: env.update(add_env) @@ -94,20 +96,15 @@ def expected_wheels(package_name, package_version, manylinux_versions=['manylinu return ['win32', 'win_amd64'] elif platform == 'macos': - if macosx_deployment_target is not None: - tag = macosx_deployment_target.replace(".", "_") - tag1 = macosx_deployment_target.replace(".", "_") - else: - tag = os.environ.get("MACOSX_DEPLOYMENT_TARGET", "10_6").replace(".", "_") - tag1 = os.environ.get("MACOSX_DEPLOYMENT_TARGET", "10_9").replace(".", "_") def get_platform_tags(python_abi_tag): if python_abi_tag == 'cp38-cp38': - return ['macosx_' + tag1 + '_x86_64'] + return ['macosx_' + (macosx_deployment_target or "10.9").replace(".", "_") + '_x86_64'] else: - return ['macosx_' + tag + '_intel'] + return ['macosx_' + (macosx_deployment_target or "10.6").replace(".", "_") + '_intel'] else: raise Exception('unsupported platform') + templates = [] for python_abi_tag in python_abi_tags: for platform_tag in get_platform_tags(python_abi_tag): From 984df80ec9029a8c6106afb9e93fb4d038b73c81 Mon Sep 17 00:00:00 2001 From: Grzegorz Bokota Date: Sat, 21 Dec 2019 13:58:55 +0100 Subject: [PATCH 6/8] change pinned version of wheel --- cibuildwheel/macos.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cibuildwheel/macos.py b/cibuildwheel/macos.py index 361e8d2e..d1b98b93 100644 --- a/cibuildwheel/macos.py +++ b/cibuildwheel/macos.py @@ -97,7 +97,7 @@ def build(project_dir, output_dir, test_command, test_requires, test_extras, bef assert os.path.exists(os.path.join(installation_bin_path, 'pip')) call(['pip', '--version'], env=env) call(['pip', 'install', '--upgrade', 'setuptools'], env=env) - call(['pip', 'install', 'git+https://github.com/Czaki/wheel.git@1a907d36eb4921efa0c9e7dce6b570aa5f84d7f5'], env=env) + call(['pip', 'install', 'git+https://github.com/Czaki/wheel.git@0dd9f3e66893517ec4c9521db523ea3a899a5148'], env=env) call(['pip', 'install', 'delocate'], env=env) # run the before_build command From 2473c405bc7bf9dda9191c59c9d9b8ad913cbdd5 Mon Sep 17 00:00:00 2001 From: Grzegorz Bokota Date: Tue, 14 Jan 2020 19:48:47 +0100 Subject: [PATCH 7/8] again change pinned version of wheel --- cibuildwheel/macos.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cibuildwheel/macos.py b/cibuildwheel/macos.py index d1b98b93..7ca603c0 100644 --- a/cibuildwheel/macos.py +++ b/cibuildwheel/macos.py @@ -97,7 +97,7 @@ def build(project_dir, output_dir, test_command, test_requires, test_extras, bef assert os.path.exists(os.path.join(installation_bin_path, 'pip')) call(['pip', '--version'], env=env) call(['pip', 'install', '--upgrade', 'setuptools'], env=env) - call(['pip', 'install', 'git+https://github.com/Czaki/wheel.git@0dd9f3e66893517ec4c9521db523ea3a899a5148'], env=env) + call(['pip', 'install', 'git+https://github.com/pypa/wheel.git@882650d0fd2fadd75f4ea7c5d6b62f15f81b398a'], env=env) call(['pip', 'install', 'delocate'], env=env) # run the before_build command From b64b106fbdb2451a120fdef9fd9cd96947e4a3d3 Mon Sep 17 00:00:00 2001 From: Grzegorz Bokota Date: Mon, 27 Jan 2020 09:36:41 +0100 Subject: [PATCH 8/8] use pypi wheel --- cibuildwheel/macos.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/cibuildwheel/macos.py b/cibuildwheel/macos.py index 7ca603c0..d7a50d0c 100644 --- a/cibuildwheel/macos.py +++ b/cibuildwheel/macos.py @@ -96,9 +96,7 @@ def build(project_dir, output_dir, test_command, test_requires, test_extras, bef call(['python', get_pip_script, '--no-setuptools', '--no-wheel'], env=env, cwd="/tmp") assert os.path.exists(os.path.join(installation_bin_path, 'pip')) call(['pip', '--version'], env=env) - call(['pip', 'install', '--upgrade', 'setuptools'], env=env) - call(['pip', 'install', 'git+https://github.com/pypa/wheel.git@882650d0fd2fadd75f4ea7c5d6b62f15f81b398a'], env=env) - call(['pip', 'install', 'delocate'], env=env) + call(['pip', 'install', '--upgrade', 'setuptools', 'wheel', 'delocate'], env=env) # run the before_build command if before_build: