Skip to content

Commit

Permalink
Add python2 generation (#283)
Browse files Browse the repository at this point in the history
* Add some code to generate python 2 bindings

* Update CMakeLists.txt to include swig directory

* update cmakeLists to load swig directory on build_python2

* updates to only allow one python interface to build (python3 will take priority)
  • Loading branch information
phlptp authored Apr 26, 2018
1 parent 4bafb0e commit e36ceb7
Show file tree
Hide file tree
Showing 6 changed files with 221 additions and 4 deletions.
15 changes: 11 additions & 4 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -43,25 +43,32 @@ set_property(GLOBAL PROPERTY USE_FOLDERS ON)
OPTION(BUILD_HELICS_EXAMPLES "Enable the example Executables to be built" ON)

OPTION(BUILD_PYTHON_INTERFACE "Build Python extension" OFF)
OPTION(BUILD_PYTHON2_INTERFACE "Build Python2.7 extension(Requires swig and will not build if python interface is active)" OFF)
OPTION(BUILD_MATLAB_INTERFACE "Build Matlab Extension" OFF)
OPTION(BUILD_OCTAVE_INTERFACE "Build Octave extension (very experimental)" OFF)
OPTION(BUILD_JAVA_INTERFACE "Build Java extension" OFF)

if (BUILD_PYTHON_INTERFACE OR BUILD_PYTHON2_INTERFACE OR BUILD_MATLAB_INTERFACE OR BUILD_JAVA_INTERFACE OR BUILD_OCTAVE_INTERFACE OR BUILD_C_SHARED_LIB OR BUILD_CXX_SHARED_LIB)
set(INTERFACE_BUILD ON)
else()
set(INTERFACE_BUILD OFF)
endif()

OPTION(DISABLE_SWIG "Disable the use of swig to generate interface code and use repo code" OFF)

OPTION(BUILD_C_SHARED_LIB "Build the Shared Libraries with a C interface" ON)
IF(NOT MSVC)
OPTION(BUILD_CXX_SHARED_LIB "Build a Shared Libraries of the CXX interface gcc/clang only" OFF)
ENDIF()

if (BUILD_PYTHON_INTERFACE OR BUILD_MATLAB_INTERFACE OR BUILD_JAVA_INTERFACE OR BUILD_OCTAVE_INTERFACE OR BUILD_C_SHARED_LIB OR BUILD_CXX_SHARED_LIB)
if (INTERFACE_BUILD OR BUILD_C_SHARED_LIB OR BUILD_CXX_SHARED_LIB)
set(BUILD_SHARED_LIBS ON)
else()
OPTION(USE_POSITION_INDEPENDENT_CODE "Build the libraries with Position independent code Useful if only building the static library and it will be used later in a shared library" OFF)
endif()


if (BUILD_PYTHON_INTERFACE OR BUILD_MATLAB_INTERFACE OR BUILD_JAVA_INTERFACE OR BUILD_OCTAVE_INTERFACE OR BUILD_C_SHARED_LIB)
if (INTERFACE_BUILD OR BUILD_C_SHARED_LIB)
set(BUILT_C_SHARED_LIB ON)
endif()

Expand Down Expand Up @@ -328,7 +335,7 @@ IF(BUILD_SHARED_LIBS AND NOT BUILD_CXX_SHARED_LIB)
ENDIF()

IF(BUILD_CXX_SHARED_LIB)
if (BUILD_C_SHARED_LIB OR BUILD_PYTHON_INTERFACE OR BUILD_JAVA_INTERFACE OR BUILD_MATLAB_INTERFACE OR BUILD_OCTAVE_INTERFACE)
if (BUILD_C_SHARED_LIB OR INTERFACE_BUILD)
message(WARNING "Building the CXX shared library and C shared library in the same build is not advisable due to conflicting symbol visibility guidelines and is unlikely to work very well")
endif()

Expand Down Expand Up @@ -400,7 +407,7 @@ if (ENABLE_CLANG_TOOLS)
include(clang-cxx-dev-tools)
endif(ENABLE_CLANG_TOOLS)

if (BUILD_PYTHON_INTERFACE OR BUILD_MATLAB_INTERFACE OR BUILD_JAVA_INTERFACE OR BUILD_OCTAVE_INTERFACE)
if (INTERFACE_BUILD)
add_subdirectory(swig)
endif()

Expand Down
5 changes: 5 additions & 0 deletions swig/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ INCLUDE_DIRECTORIES("${PROJECT_SOURCE_DIR}/src/helics/shared_api_library")

if (BUILD_PYTHON_INTERFACE)
ADD_SUBDIRECTORY(python)
if( BUILD_PYTHON2_INTERFACE)
message(WARNING "PYTHON2 interface will not be built since python 3 build is active")
endif()
elseif (BUILD_PYTHON2_INTERFACE)
ADD_SUBDIRECTORY(python2)
endif()

if (BUILD_JAVA_INTERFACE)
Expand Down
65 changes: 65 additions & 0 deletions swig/python2/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@

IF (DISABLE_SWIG OR NOT SWIG_EXECUTABLE)

MESSAGE(ERROR "Python 2 build requires swig")

ENDIF()

# https://stackoverflow.com/a/3818084/5451769
if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
# Update if necessary
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-long-long -Wno-overlength-strings -Wno-ignored-attributes")
endif()

find_package(PythonLibs 2 REQUIRED )

INCLUDE_DIRECTORIES(${PYTHON_INCLUDE_PATH})
INCLUDE_DIRECTORIES(${PYTHON_INCLUDE_DIRS})

SET(CMAKE_SWIG_FLAGS "")

set(CMAKE_MACOSX_RPATH 1)
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}")

SET_PROPERTY(SOURCE ../helics.i PROPERTY SWIG_MODULE_NAME helics)


IF (${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} VERSION_GREATER 3.7)
SWIG_ADD_LIBRARY(helics TYPE MODULE LANGUAGE python SOURCES helicsPython2.i)
ELSE ()
SWIG_ADD_MODULE(helics python helicsPython2.i)
ENDIF ()

SWIG_LINK_LIBRARIES(helics helicsSharedLib)


if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
# https://groups.google.com/a/continuum.io/d/msg/anaconda/057P4uNWyCU/Iem6OtjBCQAJ
set_target_properties(_helics PROPERTIES LINK_FLAGS "-undefined dynamic_lookup")
else ()
SWIG_LINK_LIBRARIES(helics ${PYTHON_LIBRARIES})
endif()

set_target_properties (_helics PROPERTIES FOLDER interfaces)

INSTALL(TARGETS _helics DESTINATION python COMPONENT python)

foreach(keyfile IN LISTS KEY_LIBRARY_FILES)
add_custom_command(TARGET _helics POST_BUILD # Adds a post-build event to api tests
COMMAND ${CMAKE_COMMAND} -E copy_if_different # which executes "cmake - E copy_if_different..."
"${keyfile}" # <--this is in-file
"$<TARGET_FILE_DIR:_helics>/") # <--this is out-file path
endforeach(keyfile)

add_custom_command(TARGET _helics2 POST_BUILD # Adds a post-build event to api tests
COMMAND ${CMAKE_COMMAND} -E copy_if_different # which executes "cmake - E copy_if_different..."
"$<TARGET_FILE:helicsSharedLib>" # <--this is in-file
"$<TARGET_FILE_DIR:_helics>/") # <--this is out-file path




INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/helics.py DESTINATION python COMPONENT python)
INSTALL(FILES $<TARGET_FILE:helicsSharedLib> DESTINATION python COMPONENT python)
INSTALL(FILES ${KEY_LIBRARY_FILES} DESTINATION python COMPONENT python)

3 changes: 3 additions & 0 deletions swig/python2/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# HELICS python 2.7 extension


5 changes: 5 additions & 0 deletions swig/python2/helicsPython2.i
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
%include "cstring.i"
%include "python_maps2.i"

%include "../helics.i"

132 changes: 132 additions & 0 deletions swig/python2/python_maps2.i
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@

//typemap for short maxlen strings
%typemap(in, numinputs=0) (char *outputString, int maxlen) {
$2=256;
$1=(char *)malloc(256);
}

%typemap(argout) (char *outputString, int maxlen) {
PyObject *str=PyString_FromString($1);
$result = SWIG_Python_AppendOutput($result, str);
}

%typemap(freearg) (char *outputString, int maxlen) {
if ($1) free($1);
}


//typemap for large string output with a length return in C
%typemap(in, numinputs=0) (char *outputString, int maxStringlen, int *actualLength) {
$3=&($2);
}

%typemap(freearg) (char *outputString, int maxStringlen, int *actualLength) {
if ($1) free($1);
}

%typemap(check)(char *outputString, int maxStringlen, int *actualLength) {
$2=helicsSubscriptionGetValueSize(arg1)+2;
$1 = (char *) malloc($2);
}

%typemap(argout) (char *outputString, int maxStringlen, int *actualLength) {
PyObject *o2=PyString_FromString($1);
$result = SWIG_Python_AppendOutput($result, o2);
}


//typemap for the input arguments
%typemap(in) (int argc, const char *const *argv) {
/* Check if is a list */
if (PyList_Check($input)) {
int i;
$1 = PyList_Size($input);
$2 = (char **) malloc(($1+1)*sizeof(char *));
for (i = 0; i < $1; i++) {
PyObject *o = PyList_GetItem($input,i);
if (PyString_Check(o))
$2[i] = PyString_AsString(PyList_GetItem($input,i));
else {
PyErr_SetString(PyExc_TypeError,"list must contain strings");
free($2);
return NULL;
}
}
$2[i] = 0;
} else {
PyErr_SetString(PyExc_TypeError,"not a list");
return NULL;
}
}

%typemap(freearg) (int argc, const char *const *argv) {
free((char *) $2);
}

// typemap for vector input functions
%typemap(in) (const double *vectorInput, int vectorlength) {
int i;
if (!PyList_Check($input)) {
PyErr_SetString(PyExc_ValueError,"Expected a list");
return NULL;
}
$2=PyList_Size($input);
$1 = (double *) malloc($2*sizeof(double));

for (i = 0; i < $2; i++) {
PyObject *o = PyList_GetItem($input,i);
if (PyFloat_Check(o)) {
$1[i] = PyFloat_AsDouble(o);
}else if (PyInt_Check(o))
{
$1[i] = (double)(PyInt_AsLong(o));
} else {
PyErr_SetString(PyExc_ValueError,"List elements must be numbers");
free($1);
return NULL;
}
}
}

%typemap(argout) (const double *vectorInput, int vectorlength)
{
}

%typemap(freearg) (const double *vectorInput, int vectorlength) {
if ($1) free($1);
}

// typemap for vector output functions

%typemap(arginit) (double data[], int maxlen, int *actualSize) {
$1=(double *)(NULL);
}

%typemap(in, numinputs=0) (double data[], int maxlen, int *actualSize) {
$3=&($2);
}

%typemap(freearg) (double data[], int maxlen, int *actualSize) {
if ($1) free($1);
}

// Set argument to NULL before any conversion occurs
%typemap(check)(double data[], int maxlen, int *actualSize) {
$2=helicsSubscriptionGetVectorSize(arg1);
$1 = (double *) malloc($2*sizeof(double));
}

%typemap(argout) (double data[], int maxlen, int *actualSize) {
int i;
PyObject *o2=PyList_New(*$3);
for (i = 0; i < *$3; i++) {
PyObject *o_item=PyFloat_FromDouble($1[i]);
PyList_SetItem(o2, i, o_item);
}

$result = SWIG_Python_AppendOutput($result, o2);
}

%apply (char *STRING, size_t LENGTH) { (const void *data, int inputDataLength) };

%apply (char *outputString, int maxStringlen, int *actualLength) {(void *data, int maxDatalen, int *actualSize)};

0 comments on commit e36ceb7

Please sign in to comment.