Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate Python bindings with CFFI in API mode #2

Open
wants to merge 35 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
822e01d
Bind in API-mode
robertodr Jan 7, 2020
bf0fdbd
Remove requirements.txt
robertodr Jan 7, 2020
b36f159
Appease CI, maybe?
robertodr Jan 7, 2020
9e32a3d
fix miniconda install on travis/macOS
bast Jan 10, 2020
ca6566a
fix the pip install part of the test
bast Jan 10, 2020
f926a18
No conda
robertodr Jan 10, 2020
e4a4f0e
Effing YAML
robertodr Jan 10, 2020
016db1d
Deactivate
robertodr Jan 10, 2020
ce13aa2
Try stuff
robertodr Jan 10, 2020
3f8666a
Try more stuff
robertodr Jan 10, 2020
719de57
More and more stuff
robertodr Jan 10, 2020
fc8cb8b
Disgusting
robertodr Jan 10, 2020
2c28a4b
On PATH
robertodr Jan 10, 2020
7ef348f
Compilers
robertodr Jan 10, 2020
3e9521d
Multiline
robertodr Jan 10, 2020
d312dc1
Prune
robertodr Jan 10, 2020
335584f
Braces
robertodr Jan 10, 2020
0713c59
One line
robertodr Jan 10, 2020
0d26c65
Remove suggestion
robertodr Jan 10, 2020
6956098
Suggest away
robertodr Jan 11, 2020
8b3b3e2
Are you effing with me?
robertodr Jan 11, 2020
20332e7
Get Python on macOS, maybe?
robertodr Jan 11, 2020
b007468
PIPENV_IGNORE_VIRTUALENVS=1
robertodr Jan 11, 2020
ee590c0
enraging
robertodr Jan 11, 2020
2ab3e84
Foooo
robertodr Jan 11, 2020
c934de5
Piece of s**t
robertodr Jan 11, 2020
458d623
No suggest
robertodr Jan 11, 2020
633d129
Follow docs?
robertodr Jan 11, 2020
176095e
gfortran
robertodr Jan 11, 2020
7c644ce
Piece of s**t
robertodr Jan 11, 2020
1daca9f
This makes no sense
robertodr Jan 11, 2020
90984f3
Still makes no sense
robertodr Jan 11, 2020
54ba8d0
Ufff
robertodr Jan 11, 2020
2828911
uffff
robertodr Jan 11, 2020
a7f5e55
Bah
robertodr Jan 11, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ build*/
__pycache__/
venv/
.cache/
.direnv/
.ccls-cache/
98 changes: 54 additions & 44 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,69 +1,79 @@
language: python

sudo: false

dist: bionic

env:
global:
- PIPENV_IGNORE_VIRTUALENVS=1
- PATH=$HOME/Deps/cmake/bin${PATH:+:$PATH}

matrix:
include:
- os: linux
compiler: gcc
addons: &gcc49
- name: "Context API example on Linux"
os: linux
language: python
python: 3.7
addons:
apt:
sources: ['ubuntu-toolchain-r-test']
packages: ['g++-4.9', 'gcc-4.9', 'gfortran-4.9']
packages: ['gfortran']
env:
- CXX='g++-4.9'
- CC='gcc-4.9'
- FC='gfortran-4.9'
python: 2.7
- os: linux
compiler: gcc
addons: &gcc49
apt:
sources: ['ubuntu-toolchain-r-test']
packages: ['g++-4.9', 'gcc-4.9', 'gfortran-4.9']
- CXX_COMPILER='g++'
- C_COMPILER='gcc'
- FC_COMPILER='gfortran'
- name: "Context API example on macOS"
os: osx
osx_image: xcode11.2 # Python 3.7.4 running on macOS 10.14.4
language: shell # 'language: python' is an error on Travis CI macOS
addons:
homebrew:
packages:
- gcc
- cmake
env:
- CXX='g++-4.9'
- CC='gcc-4.9'
- FC='gfortran-4.9'
python: 3.6
- os: osx
osx_image: xcode7.3
compiler: gcc
sudo: required
language: generic
- CXX_COMPILER='g++'
- C_COMPILER='gcc'
- FC_COMPILER='gfortran'

before_install:
- mkdir -p $HOME/Downloads $HOME/Deps

install:
- |
if [[ "${TRAVIS_OS_NAME}" == "osx" ]]; then
# manually install python on osx
brew update &> /dev/null
brew install python3
brew reinstall gcc
virtualenv venv
source venv/bin/activate
pip install -r requirements.txt --upgrade
if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
CMAKE_VERSION="3.14.7"
echo "-- Installing CMake ${CMAKE_VERSION}"
target_path=$HOME/Deps/cmake
cmake_url="https://cmake.org/files/v${CMAKE_VERSION%.*}/cmake-${CMAKE_VERSION}-Linux-x86_64.tar.gz"
mkdir -p "$target_path"
curl -Ls "$cmake_url" | tar -xz -C "$target_path" --strip-components=1
echo "-- Done with CMake ${CMAKE_VERSION}"
fi
- pip install -r requirements.txt --upgrade
- python --version
- pip install --upgrade pip
- pip install -U pipenv
- pipenv install --dev

before_script:
- pycodestyle account/
- pycodestyle test/test.py
- test -n $CXX && unset CXX
- test -n $CC && unset CC
- test -n $FC && unset FC

script:
- mkdir build
- pipenv run pycodestyle account/
- pipenv run pycodestyle test/test_account.py
- pipenv run cmake -H. -Bbuild -DCMAKE_CXX_COMPILER=${CXX_COMPILER} -DCMAKE_C_COMPILER=${C_COMPILER} -DCMAKE_Fortran_COMPILER=${FC_COMPILER}
- pipenv run cmake --build build -- VERBOSE=1
- cd build
- cmake ..
- make
- make test
- ctest
- pipenv run python -m pytest -rws -vv lib/python3.7
- cd ..
- ACCOUNT_LIBRARY_DIR=$PWD/build/lib ACCOUNT_INCLUDE_DIR=$PWD/account PYTHONPATH=$PWD pytest -vv test/test.py
# in the remaining part test that we can pip install the code
- mkdir test-setup-script
- cd test-setup-script
- virtualenv venv
- source venv/bin/activate
- pip install git+https://github.com/bast/context-api-example.git
- pip install /home/travis/build/dev-cafe/context-api-example/
- python -c 'import account'
- deactivate

notifications:
email: false
18 changes: 16 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,14 +1,28 @@
# define minimum cmake version
cmake_minimum_required(VERSION 2.8 FATAL_ERROR)
cmake_minimum_required(VERSION 3.14)

# project name and supported languages
project(example CXX Fortran)
project(example LANGUAGES CXX Fortran C)

include(GNUInstallDirs)

# require C++11
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# require C99
set(CMAKE_C_STANDARD 99)
set(CMAKE_C_EXTENSIONS OFF)
set(CMAKE_C_STANDARD_REQUIRED ON)

# specify where to place libraries
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)

# Python is required to get the Python interface working
find_package(Python 3.7 REQUIRED COMPONENTS Development Interpreter)
file(TO_NATIVE_PATH "lib/python${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}/account" PYMOD_INSTALL_FULLDIR)

# interface and sources
add_subdirectory(account)

Expand Down
11 changes: 11 additions & 0 deletions Pipfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true

[dev-packages]
pytest = "*"
cffi = "*"
pycodestyle = "*"

[packages]
121 changes: 113 additions & 8 deletions account/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,16 +1,121 @@
# specify where to place libraries
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
# Get file extension for Python module
execute_process(
COMMAND
"${Python_EXECUTABLE}" "-c" "from distutils import sysconfig as s;print(s.get_config_var('EXT_SUFFIX'))"
RESULT_VARIABLE _PYTHON_SUCCESS
OUTPUT_VARIABLE Python_MODULE_EXTENSION
ERROR_VARIABLE _PYTHON_ERROR_VALUE
OUTPUT_STRIP_TRAILING_WHITESPACE
)

# Call CFFI to generate bindings source file _account.cc
file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/generated)
add_custom_command(
OUTPUT
${PROJECT_BINARY_DIR}/generated/_account.c
COMMAND
${Python_EXECUTABLE} ${CMAKE_CURRENT_LIST_DIR}/builder.py
MAIN_DEPENDENCY
${CMAKE_CURRENT_LIST_DIR}/builder.py
DEPENDS
${CMAKE_CURRENT_LIST_DIR}/account.f90
${CMAKE_CURRENT_LIST_DIR}/account.h
WORKING_DIRECTORY
${PROJECT_BINARY_DIR}/generated
)

add_custom_target(
cffi-builder
ALL
DEPENDS
${PROJECT_BINARY_DIR}/generated/_account.c
)

Python_add_library(_account
MODULE
account.f90
${PROJECT_BINARY_DIR}/generated/_account.c
)

add_dependencies(_account cffi-builder)

# generate account_export.h
include(GenerateExportHeader)
generate_export_header(_account
BASE_NAME account
)

target_include_directories(_account
PRIVATE
${CMAKE_CURRENT_LIST_DIR} # where account.h lives
${CMAKE_CURRENT_BINARY_DIR} # where lsdalton_py_export.h lives
)

# implementation sources
add_subdirectory(implementation)

target_link_libraries(_account
PUBLIC
account_fortran_implementation
)

# fortran interface
add_library(fortran_c_interface account.f90)

install(
FILES
account.h
account.f90
DESTINATION
include
# RPATH fixing
file(RELATIVE_PATH _rel ${CMAKE_INSTALL_PREFIX}/${PYMOD_INSTALL_FULLDIR} ${CMAKE_INSTALL_PREFIX})
if(APPLE)
set(_RPATH "@loader_path/${_rel}")
else()
set(_RPATH "\$ORIGIN/${_rel}")
endif()

# List of Python files around the compiled extension
list(APPEND _pys
${CMAKE_CURRENT_SOURCE_DIR}/__init__.py
${CMAKE_CURRENT_SOURCE_DIR}/shim.py
)

set_target_properties(_account
PROPERTIES
SUFFIX "${Python_MODULE_EXTENSION}"
MACOSX_RPATH ON
SKIP_BUILD_RPATH OFF
BUILD_WITH_INSTALL_RPATH OFF
INSTALL_RPATH "${_RPATH}${CMAKE_INSTALL_LIBDIR}"
INSTALL_RPATH_USE_LINK_PATH ON
LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/${PYMOD_INSTALL_FULLDIR}
RESOURCE "${_pys}"
)

# Create symlinks into build tree
file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/${PYMOD_INSTALL_FULLDIR})
foreach(_py IN LISTS _pys)
get_filename_component(__py ${_py} NAME)
file(
CREATE_LINK
${_py}
${PROJECT_BINARY_DIR}/${PYMOD_INSTALL_FULLDIR}/${__py}
SYMBOLIC
)
endforeach()

install(
FILES
account.h
account.f90
${CMAKE_CURRENT_BINARY_DIR}/account_export.h
DESTINATION
${CMAKE_INSTALL_INCLUDEDIR}
)

install(
TARGETS
_account
LIBRARY
DESTINATION ${PYMOD_INSTALL_FULLDIR}/lib
RUNTIME
DESTINATION ${PYMOD_INSTALL_FULLDIR}/lib
RESOURCE
DESTINATION ${PYMOD_INSTALL_FULLDIR}
)
37 changes: 4 additions & 33 deletions account/__init__.py
Original file line number Diff line number Diff line change
@@ -1,34 +1,5 @@
from .cffi_helpers import get_lib_handle
import os
import sys
from .shim import *


def get_env(v):
_v = os.getenv(v)
if _v is None:
sys.stderr.write('ERROR: variable {0} is undefined\n'.format(v))
sys.exit(1)
return _v


_this_path = os.path.dirname(os.path.realpath(__file__))

_library_dir = os.getenv('ACCOUNT_LIBRARY_DIR')
if _library_dir is None:
_library_dir = os.path.join(_this_path, 'lib')

_include_dir = os.getenv('ACCOUNT_INCLUDE_DIR')
if _include_dir is None:
_include_dir = os.path.join(_this_path, 'include')

c_lib = get_lib_handle(['-DACCOUNT_API=', '-DACCOUNT_NOINCLUDE'],
'account.h',
'account_cpp_implementation',
_library_dir,
_include_dir)

f_lib = get_lib_handle(['-DACCOUNT_API=', '-DACCOUNT_NOINCLUDE'],
'account.h',
'account_fortran_implementation',
_library_dir,
_include_dir)
__all__ = [
"Account",
]
9 changes: 7 additions & 2 deletions account/account.h
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
#pragma once
/* CFFI would issue warning with pragma once */
#ifndef ACCOUNT_H_INCLUDED
#define ACCOUNT_H_INCLUDED

#ifndef ACCOUNT_API
#include "account_export.h"
#define ACCOUNT_API ACCOUNT_EXPORT
#endif

#ifdef __cplusplus
extern "C" {
extern "C"
{
#endif

struct account_context;
Expand All @@ -30,3 +33,5 @@ double account_get_balance(const account_context_t *context);
#ifdef __cplusplus
}
#endif

#endif /* ACCOUNT_H_INCLUDED */
Loading