From 72d78730f96402a970d8dd0841e252ef20c476c2 Mon Sep 17 00:00:00 2001 From: Benjamin Jack Date: Sun, 28 Jan 2018 14:14:49 -0600 Subject: [PATCH 1/9] bumped version number for package reorganization --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index a24c724..54753b8 100755 --- a/setup.py +++ b/setup.py @@ -94,7 +94,7 @@ def copy_test_file(self, src_file): setup( name='python_cpp_example', - version='0.1', + version='0.2', author='Benjamin Jack', author_email='benjamin.r.jack@gmail.com', description='A hybrid Python/C++ test project', From 02ca24ab1f2fd2e30b04afa388d7d2a0840aa8eb Mon Sep 17 00:00:00 2001 From: Benjamin Jack Date: Sun, 28 Jan 2018 14:16:31 -0600 Subject: [PATCH 2/9] cleaned out .eggs --- .eggs/README.txt | 6 ------ .eggs/tests-0.7-py3.6.egg | Bin 1406 -> 0 bytes 2 files changed, 6 deletions(-) delete mode 100644 .eggs/README.txt delete mode 100644 .eggs/tests-0.7-py3.6.egg diff --git a/.eggs/README.txt b/.eggs/README.txt deleted file mode 100644 index 5d01668..0000000 --- a/.eggs/README.txt +++ /dev/null @@ -1,6 +0,0 @@ -This directory contains eggs that were downloaded by setuptools to build, test, and run plug-ins. - -This directory caches those eggs to prevent repeated downloads. - -However, it is safe to delete this directory. - diff --git a/.eggs/tests-0.7-py3.6.egg b/.eggs/tests-0.7-py3.6.egg deleted file mode 100644 index e49a5a09399d6707f885b55469dc8951939d7d32..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1406 zcmWIWW@Zs#U|`^2XwR|n>0WD9wiL+w55$5%?CS2W>*?p_uOHwIVdVOq<~n2`u;;n< zC+5u8o+nPOVt4FqTL1ady4(dfTT;AV-j8LDN(_Q!U zx-4DK1ySo3%by7P{87Ye!`x{f(lWmWEAVY|^0HW;XBc{sZC>5VUUj#mQrW}*kLb*^ zez3ehFDxzq#ZUjkHSb0P{W2AZK|Y21DcCd_j4__&0JbL zy@I;EyS^4{3v&y^?7i}(qg8#M=%1hEV5%#QVUY^Qd0Ah zE8}xA^RkOUu6oRbrhAT**gg)RE;b+*L)KlAUl5;@T9%pv_Kc^lp5`-jy~sABL~T`O zfo^ePS}KNZ&t5Ov;0v_H4~Y4IxFof>q*y<n(l~WS z`(nT+kFzIFh8Jk*YMneE>~Y3B@Pn_8*2<8OOHZD9oj>or_3RmK4OXqt1VGbY{<_ z=ruDdS(mB?>b(3}xl&uQYewU-=FWIG&!sahHGg?(&OEAGwX^f&&!eKd+Ssbv+;)kI zrF~3O`?d1r%U`N1kE$O1wR37GD{B?&)a@2IMW2Jcii6%doYQ3!l<5Hm0V9(LGw!Si z3p9i5QkK(Y2$edWdEQh9!+xkhLS_0dy0P6AUP+!oZToL}2QLI{;h4MK=mP z-6D*dk8Bi5Dn>U0**T!}3j<3UZzG!lOU>xo(GxR5J1-M@(ni;do*of;+u(W$rOp6v TR#2{GU Date: Sun, 28 Jan 2018 14:21:01 -0600 Subject: [PATCH 3/9] added warning about reorganization --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index edc90a7..5f205be 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,7 @@ # python_cpp_example + +**WARNING**: I have significantly reorganized this repository, and a new blog post explaining the changes is forthcoming. If you'd like to see the version of the repository that corresponds to my original June 2017 blog post, go to [this release](https://github.com/benjaminjack/python_cpp_example/tree/v0.1). + This repository contains an example Python module which wraps C++ code. The code presented here was designed to meet four requirements: 1. Python bindings for C++ code (using [`pybind11`](http://pybind11.readthedocs.io/en/stable/index.html) and built with [CMake](http://cmake.org)) From 5cd0d1dfb77725f3d2600ea48b3210e18e2d79cc Mon Sep 17 00:00:00 2001 From: Benjamin Jack Date: Thu, 1 Feb 2018 17:22:35 -0600 Subject: [PATCH 4/9] added hello.py to demonstrate how python files can live happily alongside C++ --- src/python_cpp_example/hello.py | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 src/python_cpp_example/hello.py diff --git a/src/python_cpp_example/hello.py b/src/python_cpp_example/hello.py new file mode 100644 index 0000000..caf5928 --- /dev/null +++ b/src/python_cpp_example/hello.py @@ -0,0 +1,3 @@ + +def say_hello(): + print("Hello world!") \ No newline at end of file From 91e38783e6c2062427e0827d7abc4b6564e762b5 Mon Sep 17 00:00:00 2001 From: Benjamin Jack Date: Fri, 2 Feb 2018 14:53:28 -0600 Subject: [PATCH 5/9] added explicit message about which codebase is being tested --- tests/cpp_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/cpp_test.py b/tests/cpp_test.py index bd46ecf..5077f48 100644 --- a/tests/cpp_test.py +++ b/tests/cpp_test.py @@ -9,7 +9,7 @@ def test_cpp(self): print("\n\nTesting C++ code...") subprocess.check_call(os.path.join(os.path.dirname( os.path.relpath(__file__)), 'bin', 'python_cpp_example_test')) - print() # for prettier output + print("\nResuming Python tests...\n") if __name__ == '__main__': From 394ada5d55e0ff8a478992c9b29ffcadedc6c457 Mon Sep 17 00:00:00 2001 From: Benjamin Jack Date: Fri, 2 Feb 2018 14:57:44 -0600 Subject: [PATCH 6/9] updated README --- README.md | 6 +++--- tests/cpp_test.py | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 5f205be..c2bf849 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,5 @@ # python_cpp_example -**WARNING**: I have significantly reorganized this repository, and a new blog post explaining the changes is forthcoming. If you'd like to see the version of the repository that corresponds to my original June 2017 blog post, go to [this release](https://github.com/benjaminjack/python_cpp_example/tree/v0.1). - This repository contains an example Python module which wraps C++ code. The code presented here was designed to meet four requirements: 1. Python bindings for C++ code (using [`pybind11`](http://pybind11.readthedocs.io/en/stable/index.html) and built with [CMake](http://cmake.org)) @@ -9,7 +7,9 @@ This repository contains an example Python module which wraps C++ code. The code 3. Unit tests for Python code (using `unittest`) 4. A `setuptools` setup.py script for building, installation, and testing -Please see the [blog post that accompanies this repository](http://www.benjack.io/2017/06/12/python-cpp-tests.html) for more information. +Please see the [blog post that accompanies this repository]() for more information. + +**NOTE**: If you'd like to see the version of the repository that corresponds to my [original June 2017 blog post](http://www.benjack.io/2017/06/12/python-cpp-tests.html), go to [this release](https://github.com/benjaminjack/python_cpp_example/tree/v0.1). However, I no longer recommend using the repository structure from this old release. # Installation diff --git a/tests/cpp_test.py b/tests/cpp_test.py index 5077f48..ff1eaeb 100644 --- a/tests/cpp_test.py +++ b/tests/cpp_test.py @@ -1,5 +1,4 @@ import unittest -import python_cpp_example import subprocess import os From 7d8793496044c0e83840ff91669f70795f23deba Mon Sep 17 00:00:00 2001 From: Benjamin Jack Date: Fri, 2 Feb 2018 16:00:43 -0600 Subject: [PATCH 7/9] fixed link in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c2bf849..a218666 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ This repository contains an example Python module which wraps C++ code. The code 3. Unit tests for Python code (using `unittest`) 4. A `setuptools` setup.py script for building, installation, and testing -Please see the [blog post that accompanies this repository]() for more information. +Please see the [blog post that accompanies this repository](http://www.benjack.io/2018/02/02/python-cpp-revisited.html) for more information. **NOTE**: If you'd like to see the version of the repository that corresponds to my [original June 2017 blog post](http://www.benjack.io/2017/06/12/python-cpp-tests.html), go to [this release](https://github.com/benjaminjack/python_cpp_example/tree/v0.1). However, I no longer recommend using the repository structure from this old release. From f9d7ba6a352f84f28c1b394b7c77142a4ed97af5 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Wed, 21 Feb 2018 19:43:14 -0500 Subject: [PATCH 8/9] setup: simplify build system using scikit-build This commit introduces new dependencies: "scikit-built" and "cmake". scikit-build is a drop-in replacement to setuptools.setup function allowing to easily compile and package extensions (C/C++/Cython) by bridging CMake and setuptools. See http://scikit-build.org CMake is is an open-source, cross-platform family of tools designed to build, test and package software. See https://cmake.org Currently, scikit-build and cmake have to be explicitly (see [1]) installed on the system. This could be done by simply doing: pip install -U scikit-build cmake --- CMakeLists.txt | 5 ++- setup.py | 104 ++++++------------------------------------------- 2 files changed, 16 insertions(+), 93 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2e9477f..cb0d86f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,5 @@ cmake_minimum_required(VERSION 2.8.12) + project(python_cpp_example) SET(SOURCE_DIR "src/python_cpp_example") @@ -14,7 +15,9 @@ SET(TESTS ${SOURCES} # Generate a test executable include_directories(lib/catch/include) add_executable("${PROJECT_NAME}_test" ${TESTS}) +set_target_properties("${PROJECT_NAME}_test" PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests/bin) # Generate python module add_subdirectory(lib/pybind11) -pybind11_add_module(python_cpp_example ${SOURCES} "${SOURCE_DIR}/bindings.cpp") \ No newline at end of file +pybind11_add_module(python_cpp_example ${SOURCES} "${SOURCE_DIR}/bindings.cpp") +install(TARGETS python_cpp_example DESTINATION src/python_cpp_example) diff --git a/setup.py b/setup.py index 54753b8..868602c 100755 --- a/setup.py +++ b/setup.py @@ -1,108 +1,28 @@ #! /usr/bin/env python3 -import os -import re -import sys -import sysconfig -import platform -import subprocess +from __future__ import print_function +from os import sys, path -from distutils.version import LooseVersion -from setuptools import setup, Extension, find_packages -from setuptools.command.build_ext import build_ext -from setuptools.command.test import test as TestCommand -from shutil import copyfile, copymode +try: + from skbuild import setup +except ImportError: + print('scikit-build is required to build from source.', file=sys.stderr) + print('Please run:', file=sys.stderr) + print('', file=sys.stderr) + print(' python -m pip install scikit-build') + sys.exit(1) - -class CMakeExtension(Extension): - def __init__(self, name, sourcedir=''): - Extension.__init__(self, name, sources=[]) - self.sourcedir = os.path.abspath(sourcedir) - - -class CMakeBuild(build_ext): - def run(self): - try: - out = subprocess.check_output(['cmake', '--version']) - except OSError: - raise RuntimeError( - "CMake must be installed to build the following extensions: " + - ", ".join(e.name for e in self.extensions)) - - if platform.system() == "Windows": - cmake_version = LooseVersion(re.search(r'version\s*([\d.]+)', - out.decode()).group(1)) - if cmake_version < '3.1.0': - raise RuntimeError("CMake >= 3.1.0 is required on Windows") - - for ext in self.extensions: - self.build_extension(ext) - - def build_extension(self, ext): - extdir = os.path.abspath( - os.path.dirname(self.get_ext_fullpath(ext.name))) - cmake_args = ['-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=' + extdir, - '-DPYTHON_EXECUTABLE=' + sys.executable] - - cfg = 'Debug' if self.debug else 'Release' - build_args = ['--config', cfg] - - if platform.system() == "Windows": - cmake_args += ['-DCMAKE_LIBRARY_OUTPUT_DIRECTORY_{}={}'.format( - cfg.upper(), - extdir)] - if sys.maxsize > 2**32: - cmake_args += ['-A', 'x64'] - build_args += ['--', '/m'] - else: - cmake_args += ['-DCMAKE_BUILD_TYPE=' + cfg] - build_args += ['--', '-j2'] - - env = os.environ.copy() - env['CXXFLAGS'] = '{} -DVERSION_INFO=\\"{}\\"'.format( - env.get('CXXFLAGS', ''), - self.distribution.get_version()) - if not os.path.exists(self.build_temp): - os.makedirs(self.build_temp) - subprocess.check_call(['cmake', ext.sourcedir] + cmake_args, - cwd=self.build_temp, env=env) - subprocess.check_call(['cmake', '--build', '.'] + build_args, - cwd=self.build_temp) - # Copy *_test file to tests directory - test_bin = os.path.join(self.build_temp, 'python_cpp_example_test') - self.copy_test_file(test_bin) - print() # Add an empty line for cleaner output - - def copy_test_file(self, src_file): - ''' - Copy ``src_file`` to ``dest_file`` ensuring parent directory exists. - By default, message like `creating directory /path/to/package` and - `copying directory /src/path/to/package -> path/to/package` are displayed on standard output. Adapted from scikit-build. - ''' - # Create directory if needed - dest_dir = os.path.join(os.path.dirname( - os.path.abspath(__file__)), 'tests', 'bin') - if dest_dir != "" and not os.path.exists(dest_dir): - print("creating directory {}".format(dest_dir)) - os.makedirs(dest_dir) - - # Copy file - dest_file = os.path.join(dest_dir, os.path.basename(src_file)) - print("copying {} -> {}".format(src_file, dest_file)) - copyfile(src_file, dest_file) - copymode(src_file, dest_file) +from setuptools import find_packages setup( name='python_cpp_example', - version='0.2', + version='0.3', author='Benjamin Jack', author_email='benjamin.r.jack@gmail.com', description='A hybrid Python/C++ test project', long_description='', packages=find_packages('src'), package_dir={'':'src'}, - ext_modules=[CMakeExtension('python_cpp_example/python_cpp_example')], - cmdclass=dict(build_ext=CMakeBuild), test_suite='tests', zip_safe=False, ) From 7ea8e23b85ccc6bc59788dc3605adec58db2bc2e Mon Sep 17 00:00:00 2001 From: "Benjamin R. Jack" Date: Sun, 25 Feb 2018 19:26:46 -0600 Subject: [PATCH 9/9] Revert "setup: simplify build system using scikit-build" --- CMakeLists.txt | 5 +-- setup.py | 104 +++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 93 insertions(+), 16 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cb0d86f..2e9477f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,4 @@ cmake_minimum_required(VERSION 2.8.12) - project(python_cpp_example) SET(SOURCE_DIR "src/python_cpp_example") @@ -15,9 +14,7 @@ SET(TESTS ${SOURCES} # Generate a test executable include_directories(lib/catch/include) add_executable("${PROJECT_NAME}_test" ${TESTS}) -set_target_properties("${PROJECT_NAME}_test" PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests/bin) # Generate python module add_subdirectory(lib/pybind11) -pybind11_add_module(python_cpp_example ${SOURCES} "${SOURCE_DIR}/bindings.cpp") -install(TARGETS python_cpp_example DESTINATION src/python_cpp_example) +pybind11_add_module(python_cpp_example ${SOURCES} "${SOURCE_DIR}/bindings.cpp") \ No newline at end of file diff --git a/setup.py b/setup.py index 868602c..54753b8 100755 --- a/setup.py +++ b/setup.py @@ -1,28 +1,108 @@ #! /usr/bin/env python3 -from __future__ import print_function -from os import sys, path +import os +import re +import sys +import sysconfig +import platform +import subprocess -try: - from skbuild import setup -except ImportError: - print('scikit-build is required to build from source.', file=sys.stderr) - print('Please run:', file=sys.stderr) - print('', file=sys.stderr) - print(' python -m pip install scikit-build') - sys.exit(1) +from distutils.version import LooseVersion +from setuptools import setup, Extension, find_packages +from setuptools.command.build_ext import build_ext +from setuptools.command.test import test as TestCommand +from shutil import copyfile, copymode -from setuptools import find_packages + +class CMakeExtension(Extension): + def __init__(self, name, sourcedir=''): + Extension.__init__(self, name, sources=[]) + self.sourcedir = os.path.abspath(sourcedir) + + +class CMakeBuild(build_ext): + def run(self): + try: + out = subprocess.check_output(['cmake', '--version']) + except OSError: + raise RuntimeError( + "CMake must be installed to build the following extensions: " + + ", ".join(e.name for e in self.extensions)) + + if platform.system() == "Windows": + cmake_version = LooseVersion(re.search(r'version\s*([\d.]+)', + out.decode()).group(1)) + if cmake_version < '3.1.0': + raise RuntimeError("CMake >= 3.1.0 is required on Windows") + + for ext in self.extensions: + self.build_extension(ext) + + def build_extension(self, ext): + extdir = os.path.abspath( + os.path.dirname(self.get_ext_fullpath(ext.name))) + cmake_args = ['-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=' + extdir, + '-DPYTHON_EXECUTABLE=' + sys.executable] + + cfg = 'Debug' if self.debug else 'Release' + build_args = ['--config', cfg] + + if platform.system() == "Windows": + cmake_args += ['-DCMAKE_LIBRARY_OUTPUT_DIRECTORY_{}={}'.format( + cfg.upper(), + extdir)] + if sys.maxsize > 2**32: + cmake_args += ['-A', 'x64'] + build_args += ['--', '/m'] + else: + cmake_args += ['-DCMAKE_BUILD_TYPE=' + cfg] + build_args += ['--', '-j2'] + + env = os.environ.copy() + env['CXXFLAGS'] = '{} -DVERSION_INFO=\\"{}\\"'.format( + env.get('CXXFLAGS', ''), + self.distribution.get_version()) + if not os.path.exists(self.build_temp): + os.makedirs(self.build_temp) + subprocess.check_call(['cmake', ext.sourcedir] + cmake_args, + cwd=self.build_temp, env=env) + subprocess.check_call(['cmake', '--build', '.'] + build_args, + cwd=self.build_temp) + # Copy *_test file to tests directory + test_bin = os.path.join(self.build_temp, 'python_cpp_example_test') + self.copy_test_file(test_bin) + print() # Add an empty line for cleaner output + + def copy_test_file(self, src_file): + ''' + Copy ``src_file`` to ``dest_file`` ensuring parent directory exists. + By default, message like `creating directory /path/to/package` and + `copying directory /src/path/to/package -> path/to/package` are displayed on standard output. Adapted from scikit-build. + ''' + # Create directory if needed + dest_dir = os.path.join(os.path.dirname( + os.path.abspath(__file__)), 'tests', 'bin') + if dest_dir != "" and not os.path.exists(dest_dir): + print("creating directory {}".format(dest_dir)) + os.makedirs(dest_dir) + + # Copy file + dest_file = os.path.join(dest_dir, os.path.basename(src_file)) + print("copying {} -> {}".format(src_file, dest_file)) + copyfile(src_file, dest_file) + copymode(src_file, dest_file) setup( name='python_cpp_example', - version='0.3', + version='0.2', author='Benjamin Jack', author_email='benjamin.r.jack@gmail.com', description='A hybrid Python/C++ test project', long_description='', packages=find_packages('src'), package_dir={'':'src'}, + ext_modules=[CMakeExtension('python_cpp_example/python_cpp_example')], + cmdclass=dict(build_ext=CMakeBuild), test_suite='tests', zip_safe=False, )