Commit 77744c74 authored by Amir MOHAMMADI's avatar Amir MOHAMMADI

Merge branch 'sdkroot' into 'master'

Augments the extension for building C/C++ bindings with tests for SDKROOT and MACOSX_DEPLOYMENT_TARGET

See merge request !99
parents 8af04533 3a0c6ef9
Pipeline #29638 passed with stages
in 9 minutes and 18 seconds
......@@ -36,6 +36,11 @@ from .rc_config import _loadrc
__version__ = pkg_resources.require(__name__)[0].version
# Loads the rc user preferences
rc = _loadrc()
"""The content of the global configuration file loaded as a dictionary.
The value for any non-existing key is ``None``."""
def check_packages(packages):
"""Checks if the requirements for the given packages are satisfied.
......@@ -406,12 +411,45 @@ class Extension(DistutilsExtension):
# Compilation options for macOS builds
if platform.system() == 'Darwin':
sdkroot = os.environ.get('SDKROOT')
if sdkroot is not None and sdkroot:
parameters['extra_compile_args'] = ['-isysroot', sdkroot] + \
parameters['extra_compile_args']
parameters['extra_compile_args'] += ['-Wno-#warnings']
target = os.environ.get('MACOSX_DEPLOYMENT_TARGET')
if target is None: #not set on the environment, try resource
target = rc['bob.extension.macosx_deployment_target']
if target is None or not target:
raise EnvironmentError('${MACOSX_DEPLOYMENT_TARGET} environment ' \
'variable is **NOT** set - To avoid this error, either set ' \
'${MACOSX_DEPLOYMENT_TARGET} or Bob\'s resource ' \
'"bob.extension.macosx_deployment_target" to a suitable ' \
'value (e.g. ' \
'"bob config set bob.extension.macosx_deployment_target 10.9") ' \
'before trying to build C/C++ extensions')
os.environ.setdefault('MACOSX_DEPLOYMENT_TARGET', target)
sdkroot = os.environ.get('SDKROOT', os.environ.get('CONDA_BUILD_SYSROOT'))
if sdkroot is None: #not set on the environment, try resource
sdkroot = rc['bob.extension.macosx_sdkroot']
if sdkroot is None or not sdkroot:
raise EnvironmentError('${SDKROOT} environment variable is **NOT** ' \
'set - To avoid this error, either set ${SDKROOT} or Bob\'s ' \
'resource "bob.extension.macosx_sdkroot" to a suitable value ' \
'(e.g. "bob config ' \
'set bob.extension.macosx_sdkroot /opt/MacOSX10.9.sdk")' \
'before trying to build C/C++ extensions')
# test for the compatibility between deployment target and sdk root
sdkversion = os.path.basename(sdkroot)[6:-4]
if sdkversion != target:
logger.warn('There is an inconsistence between the value ' \
'set for ${MACOSX_DEPLOYMENT_TARGET} (%s) and ' \
'${SDKROOT}/${CONDA_BUILD_SYSROOT} (%s) - Fix it by properly ' \
'setting up these environment variables via Bob\'s ' \
'configuration (refer to bob.extension\'s user guide for ' \
'detailed instructions)', target, sdkroot)
os.environ.setdefault('SDKROOT', sdkroot)
parameters['extra_compile_args'] = \
['-mmacosx-version-min=%s' % target] + ['-isysroot', sdkroot] + \
parameters['extra_compile_args'] + ['-Wno-#warnings']
user_includes = kwargs.get('include_dirs', [])
self.pkg_includes = []
......@@ -830,10 +868,5 @@ def get_config(package=__name__, externals=None, api_version=None):
return retval.strip()
# Loads the rc user preferences
rc = _loadrc()
"""The content of the global configuration file loaded as a dictionary.
The value for any non-existing key is ``None``."""
# gets sphinx autodoc done right - don't remove it
__all__ = [_ for _ in dir() if not _.startswith('_')]
......@@ -6,9 +6,12 @@ C/C++ libraries in your package
Typically, |project|'s core packages include both a pure C++ library as well as Python bindings for the C++ code.
If you want to provide a library with pure C++ code in your package as well, you can use the :py:class:`bob.extension.Library` class.
It will automatically compile your C/C++ code using `CMake <http://www.cmake.org>`_ into a shared library that you can link to your own C/C++-Python bindings,
as well as in the C++ code of other C++/Python packages. Again, a complete example can be downloaded via:
If you want to provide a library with pure C++ code in your package as well,
you can use the :py:class:`bob.extension.Library` class. It will automatically
compile your C/C++ code using `CMake <http://www.cmake.org>`_ into a shared
library that you can link to your own C/C++-Python bindings, as well as in the
C++ code of other C++/Python packages. Again, a complete example can be
downloaded via:
.. code-block:: sh
......@@ -63,7 +66,7 @@ If you would like to generate a Library out of your C++ code, simply add it in t
...
)
Again, we use the overloaded library class
:py:class:`bob.blitz.extension.Library` instead of the
......@@ -96,7 +99,7 @@ No worries, if the library is not used in the extension, the linker should be ab
.. note::
You can also export a library without bindings, for it to be used in other C++/Python packages.
---------------------
Building your package
......@@ -126,4 +129,7 @@ Again, after compilation this directory can be safely removed.
Another environment variable enables parallel compilation of C or C++ code.
Use ``BOB_BUILD_PARALLEL=X`` (where ``X`` is the number of parallel processes you want) to enable parallel building.
.. note::
For macOS-based builds, you may need to setup additional environment
variables **before** successfully building libraries. Refer to the section
:ref:`extension-c++` for details.
......@@ -26,7 +26,7 @@ You can check it out by:
Setting up your package
-----------------------
Typically, Python extensions written in C/C++ for Bob should use a set of standard APIs allowing C++ Blitz++ Arrays to be transparently converted to Python NumPy Arrays.
Typically, Python extensions written in C/C++ for Bob should use a set of standard APIs allowing C++ Blitz++ Arrays to be transparently converted to Python NumPy Arrays.
The build of your package will therefore depend on, at least, two packages: (1) bob.extension (this package): will provide build instructions and resources for defining and building your extension (2) bob.blitz: will provide a bridge between pure C++ code, depending on Blitz++ Arrays and NumPy arrays.
To be able to import ``bob.extension`` and ``bob.blitz`` in the setup.py, we need to include some code:
......@@ -46,7 +46,7 @@ In our example, we only depend on ``bob.blitz``, and we can leave the list empty
.. warning::
``bob.blitz`` is required in all C++/Python packages since it contains all the mechanisms
``bob.blitz`` is required in all C++/Python packages since it contains all the mechanisms
to deal with arrays amongst other things.
As the second step, we need to add some lines in the header of the file to tell the ``setuptools`` system to compile our library with our ``Extension`` class:
......@@ -122,7 +122,7 @@ Basically, there are two C++ files for our extension.
In ``bob/example/extension/main.cpp``, we define the Python bindings to that function.
Finally, the function ``reverse`` from the module ``_library`` is imported into our module in the ``bob/example/extension/__init__.py`` file.
..
..
including the creation of a complete Python module called ``_library``.
Additionally, we give a short example of how to use our documentation classes provided in this module (see below for more details).
......@@ -134,7 +134,7 @@ Finally, the function ``reverse`` from the module ``_library`` is imported into
Building your package
---------------------
To compile your C++ Python bindings and the corresponding C++ implementation,
To compile your C++ Python bindings and the corresponding C++ implementation,
just do:
.. code-block:: sh
......@@ -146,6 +146,58 @@ just do:
By default, we compile the source code (of this and **all dependent packages**, both the ones installed as ``eggs``, and the ones developed using ``mr.developer``) in debug mode.
If you want to change that, switch the according flag in the ``buildout.cfg`` to ``debug = False``, and the compilation will be done with optimization flags and C++ exception handling enabled.
.. note::
For macOS-based builds, one also needs to ensure the environment variables
MACOSX_DEPLOYMENT_TARGET and SDKROOT are properly set. This is
automatically handled for conda-build based runs. If you are using
buildout or any other setuptools-based system (such as pip installs) to
build your package, you should ensure that is the case with one of these 2
methods (more to least recommended):
1. You set the RC variables (see: :ref:`bob.extension.rc`)
`bob.extension.macosx_deployment_target` and
`bob.extension.macosx_sdkroot` to suitable values. Example:
.. code-block:: sh
$ bob config get bob.extension.macosx_deployment_target
Error: The requested key `bob.extension.macosx_deployment_target` does not exist
$ bob config set bob.extension.macosx_deployment_target "10.9"
$ bob config get bob.extension.macosx_sdkroot
Error: The requested key `bob.extension.macosx_sdkroot` does not exist
$ bob config set bob.extension.macosx_sdkroot "/opt/MacOSX10.9.sdk"
With this method you set the default for your particular machine. It is
the recommended way to set up such variables as those settings do not
affect builds in other machines and are preserved across package builds,
guaranteeing uniformity.
2. You set the environment variables directly on the current environment.
Example:
.. code-block:: sh
$ export MACOSX_DEPLOYMENT_TARGET="10.9"
$ export SDKROOT="/opt/MacOSX10.9.sdk"
Note that this technique is the least ephemeral from all available
options. As soon as you leave the current environment, the variables
will not be available anymore.
**Precedence**: Values set on the environment have precedence over values
set on your Bob RC configuration.
**Compatibility**: We recommend you check our stock
`conda_build_config.yaml` for ensuring cross-package compatibility
(currently available through our admin package "bob.devtools"). At the time
of writing, we use a "10.9" macOS SDK for Bob packages. That may change in
the future.
**Obtaining an SDK**: We recommend `Phracker macOS SDKs available on Github
<https://github.com/phracker/MacOSX-SDKs>`_. Install the SDK on
``/opt/MacOSX<version>.sdk``.
Now, we can use the script ``./bin/bob_example_extension_reverse.py`` (that we have registered in the ``setup.py``) to reverse a list of floats, using the C++ implementation of the ``reverse`` function:
......@@ -213,5 +265,3 @@ After including the above mentioned header, we also re-define the functions
:c:func:`PyInt_Check`, :c:func:`PyInt_AS_LONG`, :c:func:`PyString_Check` and
:c:func:`PyString_AS_STRING` (which doesn't exist in the bindings for Python3)
so that they can be used in bindings for both Python2 and Python3.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment