From 6d58decf51dac330ebd00279d8923ec4d2428f5f Mon Sep 17 00:00:00 2001 From: Ilya Matiach Date: Wed, 31 Jan 2018 01:20:41 -0500 Subject: [PATCH] Adding SWIG Java wrappers to LightGBM (#1223) * Adding Java wrappers to LightGBM by updating CMakeLists and adding SWIG file * Set SWIG generation to OFF by default * Added -package option to SWIG_FLAGS * Fixed jar structure to have class files in proper location * removed link with OpenMP flags --- CMakeLists.txt | 34 +++++++++++++++- swig/lightgbmlib.i | 99 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 swig/lightgbmlib.i diff --git a/CMakeLists.txt b/CMakeLists.txt index b5742e35a..261e12c08 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,7 @@ PROJECT(lightgbm) OPTION(USE_MPI "MPI based parallel learning" OFF) OPTION(USE_OPENMP "Enable OpenMP" ON) OPTION(USE_GPU "Enable GPU-acclerated training (EXPERIMENTAL)" OFF) +OPTION(USE_SWIG "Enable SWIG to generate Java API" OFF) if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "4.8.2") @@ -28,6 +29,25 @@ if(APPLE) OPTION(APPLE_OUTPUT_DYLIB "Output dylib shared library" OFF) endif() +if(USE_SWIG) + find_package(SWIG REQUIRED) + find_package(Java REQUIRED) + find_package(JNI REQUIRED) + include(UseJava) + include(UseSWIG) + set(SWIG_CXX_EXTENSION "cxx") + set(SWIG_EXTRA_LIBRARIES "") + set(SWIG_JAVA_EXTRA_FILE_EXTENSIONS ".java" "JNI.java") + set(SWIG_MODULE_JAVA_LANGUAGE "JAVA") + set(SWIG_MODULE_JAVA_SWIG_LANGUAGE_FLAG "java") + set(CMAKE_SWIG_OUTDIR "${CMAKE_CURRENT_BINARY_DIR}/java") + FILE(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/com/microsoft/ml/lightgbm/linux/x86_64") + include_directories(Java_INCLUDE_DIRS) + include_directories(JNI_INCLUDE_DIRS) + include_directories($ENV{JAVA_HOME}/include) + include_directories($ENV{JAVA_HOME}/include/linux) +endif(USE_SWIG) + if(USE_MPI) find_package(MPI REQUIRED) ADD_DEFINITIONS(-DUSE_MPI) @@ -125,7 +145,19 @@ file(GLOB SOURCES ) add_executable(lightgbm src/main.cpp ${SOURCES}) -add_library(_lightgbm SHARED src/c_api.cpp src/lightgbm_R.cpp ${SOURCES}) +add_library(_lightgbm SHARED src/c_api.cpp src/lightgbm_R.cpp ${SOURCES}) + +if(USE_SWIG) + set_property(SOURCE swig/lightgbmlib.i PROPERTY CPLUSPLUS ON) + LIST(APPEND swig_options -package com.microsoft.ml.lightgbm) + set_property(SOURCE swig/lightgbmlib.i PROPERTY SWIG_FLAGS "${swig_options}") + swig_add_module(_lightgbm_swig java swig/lightgbmlib.i) + swig_link_libraries(_lightgbm_swig _lightgbm) + add_custom_command(TARGET _lightgbm_swig POST_BUILD + COMMAND "${Java_JAVAC_EXECUTABLE}" -d . java/*.java + COMMAND cp "${PROJECT_SOURCE_DIR}/*.so" com/microsoft/ml/lightgbm/linux/x86_64 + COMMAND "${Java_JAR_EXECUTABLE}" -cf lightgbmlib.jar com) +endif(USE_SWIG) if(MSVC) set_target_properties(_lightgbm PROPERTIES OUTPUT_NAME "lib_lightgbm") diff --git a/swig/lightgbmlib.i b/swig/lightgbmlib.i new file mode 100644 index 000000000..2b3269260 --- /dev/null +++ b/swig/lightgbmlib.i @@ -0,0 +1,99 @@ +/* lightgbmlib.i */ +%module lightgbmlib +%ignore LGBM_BoosterSaveModelToString; +%{ +/* Includes the header in the wrapper code */ +#include "../include/LightGBM/export.h" +#include "../include/LightGBM/utils/log.h" +#include "../include/LightGBM/c_api.h" +%} + +/* header files */ +%include "../include/LightGBM/export.h" +%include "../include/LightGBM/c_api.h" +%include "cpointer.i" +%include "carrays.i" + +%inline %{ + char * LGBM_BoosterSaveModelToStringSWIG(BoosterHandle handle, + int num_iteration, + int64_t buffer_len, + int64_t* out_len) { + char* dst = new char[buffer_len]; + int result = LGBM_BoosterSaveModelToString(handle, num_iteration, buffer_len, out_len, dst); + if (result != 0) { + return nullptr; + } + return dst; + } +%} + +%pointer_functions(int, intp) +%pointer_functions(long, longp) +%pointer_functions(double, doublep) +%pointer_functions(float, floatp) +%pointer_functions(int64_t, int64_tp) +%pointer_functions(int32_t, int32_tp) + +%pointer_cast(int64_t *, long *, int64_t_to_long_ptr) +%pointer_cast(int64_t *, double *, int64_t_to_double_ptr) +%pointer_cast(int32_t *, int *, int32_t_to_int_ptr) +%pointer_cast(long *, int64_t *, long_to_int64_t_ptr) +%pointer_cast(double *, int64_t *, double_to_int64_t_ptr) +%pointer_cast(double *, void *, double_to_voidp_ptr) +%pointer_cast(int *, int32_t *, int_to_int32_t_ptr) +%pointer_cast(float *, void *, float_to_voidp_ptr) + +%array_functions(double, doubleArray) +%array_functions(float, floatArray) +%array_functions(int, intArray) +%array_functions(long, longArray) + +/* Custom pointer manipulation template */ +%define %pointer_manipulation(TYPE,NAME) +%{ + static TYPE *new_##NAME() { %} + %{ TYPE* NAME = new TYPE; return NAME; %} + %{} + + static void delete_##NAME(TYPE *self) { %} + %{ if (self) delete self; %} + %{} + %} + +TYPE *new_##NAME(); +void delete_##NAME(TYPE *self); + +%enddef + +%define %pointer_dereference(TYPE,NAME) +%{ + static TYPE NAME ##_value(TYPE *self) { + TYPE NAME = *self; + return NAME; + } +%} + +TYPE NAME##_value(TYPE *self); + +%enddef + +%define %pointer_handle(TYPE,NAME) +%{ + static TYPE* NAME ##_handle() { %} + %{ TYPE* NAME = new TYPE; *NAME = (TYPE)operator new(sizeof(int*)); return NAME; %} + %{} +%} + +TYPE *NAME##_handle(); + +%enddef + +%pointer_manipulation(void*, voidpp) + +/* Allow dereferencing of void** to void* */ +%pointer_dereference(void*, voidpp) + +/* Allow retrieving handle to void** */ +%pointer_handle(void*, voidpp) +