You need to sign in or sign up before continuing.
Commit 0866d03a authored by Davis King's avatar Davis King

Added cmake scripts and some related tooling that makes it easy to call C++

code from java.
parent 96881874
cmake_minimum_required (VERSION 2.8.12)
project (myproject)
set(java_package_name net.dlib)
set(source_files
)
include(../cmake_utils/use_cpp_11.cmake)
include(../cmake_utils/release_build_by_default)
include_directories(
.
)
# Additional dependencies
#include(../../dlib/cmake)
#set(additional_link_libraries dlib)
# tell swig to put the output files into the local folder.
set(install_target_output_folder .)
include(cmake_swig_jni)
# This file is used to create SWIG based JNI interfaces to C++ code. You use
# it by defining some CMake variables and then include(cmake_swig_jni). You
# would make a CMakeLists.txt file that looks like the following:
#
# cmake_minimum_required (VERSION 2.8.4)
# project (example)
# set(java_package_name "org.mycompany")
# set(source_files
# your_cpp_source.cpp
# more_cpp_source.cpp
# )
#
# ### We might need to link our code to some other C++ library like dlib. You
# ### can do that by setting additional_link_libraries. Here is an example of
# ### linking to dlib:
# include(../../dlib/dlib/cmake)
# set(additional_link_libraries dlib)
#
# ### tell swig to put the output files into the parent folder of your CMakeLists.txt
# ### file when you run make install.
# set(install_target_output_folder ..)
# include(cmake_swig_jni)
################################################################################
################################################################################
# IMPLEMENTATION DETAILS
################################################################################
################################################################################
cmake_minimum_required (VERSION 2.8.4)
# This block of code tries to figure out what the JAVA_HOME environment
# variable should be by looking at the folder that contains the java
# executable.
if (NOT DEFINED ENV{JAVA_HOME})
message(STATUS "JAVA_HOME environment variable not set, trying to guess it...")
find_program(JAVA_EXECUTABLE java)
# Resolve symbolic links, hopefully this will give us a path in the proper
# java home directory.
get_filename_component(JAVA_EXECUTABLE ${JAVA_EXECUTABLE} REALPATH)
# Pick out the parent directories
get_filename_component(JAVA_PATH1 ${JAVA_EXECUTABLE} PATH)
get_filename_component(JAVA_PATH2 ${JAVA_PATH1} PATH)
get_filename_component(JAVA_PATH3 ${JAVA_PATH2} PATH)
# and search them for include/jni.h. If we find that then we probably have
# a good java home candidate.
find_path(AUTO_JAVA_HOME include/jni.h
PATHS
${JAVA_PATH1}
${JAVA_PATH2}
${JAVA_PATH3}
"C:/Program Files/Java/jdk*"
"C:/Program Files (x86)/Java/jdk*"
)
if (AUTO_JAVA_HOME)
set(ENV{JAVA_HOME} ${AUTO_JAVA_HOME})
message(STATUS "Using JAVA_HOME OF " ${AUTO_JAVA_HOME})
else()
message(FATAL_ERROR "Couldn't find a folder for JAVA_HOME. You must set the JAVA_HOME environment variable before running CMake.")
endif()
endif()
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/lib")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_BINARY_DIR}/lib")
find_package(SWIG REQUIRED)
find_package(Java REQUIRED)
find_package(JNI REQUIRED)
include(UseSWIG)
macro (add_global_switch def_name )
if (NOT CMAKE_CXX_FLAGS MATCHES "${def_name}")
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${def_name}"
CACHE STRING "Flags used by the compiler during all C++ builds."
FORCE)
endif ()
if (NOT CMAKE_C_FLAGS MATCHES "${def_name}")
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${def_name}"
CACHE STRING "Flags used by the compiler during all C builds."
FORCE)
endif ()
endmacro()
# SWIG doesn't work if optimizations are enabled and strict aliasing is not
# turned off. This is a little wonky but it's how SWIG is.
if (CMAKE_COMPILER_IS_GNUCXX)
add_definitions(-fno-strict-aliasing)
endif()
if (UNIX)
# we need to make sure all the code is compiled with -fPIC. In particular,
# it's important that all the code for the whole project is, not just the
# stuff immediately compiled by us in this cmake file. So we add -fPIC to
# the top level cmake flags variables.
add_global_switch(-fPIC)
endif()
string(REGEX REPLACE "\\." "/" package_path ${java_package_name})
string(REGEX REPLACE "\\..*" "" package_root_name ${java_package_name})
set(CMAKE_SWIG_FLAGS -package ${java_package_name})
set(CMAKE_SWIG_OUTDIR ${CMAKE_CURRENT_BINARY_DIR}/lib/java_src/${package_path})
set(output_library_name ${PROJECT_NAME})
# Create the swig.i interface file that swig will run on. We do it here in
# the cmake script because this lets us automatically include the correct
# output library name into the call to System.loadLibrary().
FILE(WRITE ${CMAKE_CURRENT_BINARY_DIR}/swig.i
"
// Put the global functions in our api into a java class called global.
%module global
%{
#include \"swig_api.h\"
#include <exception>
%}
// Convert all C++ exceptions into java.lang.Exception
%exception {
try {
$action
} catch(std::exception& e) {
jclass clazz = jenv->FindClass(\"java/lang/Exception\");
jenv->ThrowNew(clazz, e.what());
return $null;
}
}
%pragma(java) jniclasscode=%{
static {
try {
System.loadLibrary(\"${output_library_name}\");
} catch (UnsatisfiedLinkError e) {
System.err.println(\"Native code library failed to load. \\n\" + e);
System.exit(1);
}
}
%}
%include \"swig_api.h\"
"
)
# There is a bug in CMake's Swig scripts that causes the build to fail if the
# binary folder doesn't contain a folder with the same name as the binary dir.
# So we make a subfolder of the same name to avoid that bug.
get_filename_component(binary_dir_name "${CMAKE_CURRENT_BINARY_DIR}" NAME)
FILE(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${binary_dir_name}")
set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/swig.i PROPERTIES CPLUSPLUS ON)
swig_add_module(${output_library_name} java ${CMAKE_CURRENT_BINARY_DIR}/swig.i ${source_files})
include_directories(${JNI_INCLUDE_DIRS})
swig_link_libraries(${output_library_name} ${additional_link_libraries})
# Things to delete when "make clean" is run.
set(clean_files
${CMAKE_CURRENT_BINARY_DIR}/intermediate_files_compiled
${CMAKE_CURRENT_BINARY_DIR}/lib/java_src
)
set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES "${clean_files}")
# Compile the java files into a jar file and stick it in the lib folder.
add_custom_command(TARGET ${output_library_name}
POST_BUILD
COMMAND cmake -E echo "compiling Java files..."
COMMAND cmake -E make_directory "${CMAKE_CURRENT_BINARY_DIR}/intermediate_files_compiled"
COMMAND ${Java_JAVAC_EXECUTABLE} ${CMAKE_SWIG_OUTDIR}/*.java -d "${CMAKE_CURRENT_BINARY_DIR}/intermediate_files_compiled"
COMMAND cmake -E echo "Making jar file..."
COMMAND ${Java_JAR_EXECUTABLE} cvf "${CMAKE_CURRENT_BINARY_DIR}/lib/${PROJECT_NAME}.jar" -C "${CMAKE_CURRENT_BINARY_DIR}/intermediate_files_compiled" ${package_root_name}
)
#if the including cmake script set the install_target_output_folder variable
#then make it so we install the compiled library and jar into that folder
if (install_target_output_folder)
# Determine the path to our CMakeLists.txt file.
# There is either a bug (or break in compatability maybe) between versions
# of cmake that cause the or expression in this regular expression to be
# necessary.
string(REGEX REPLACE "(cmake_swig_jni|CMakeLists.txt)$" "" base_path ${CMAKE_PARENT_LIST_FILE})
# The directory we will write the output files to.
set(install_dir "${base_path}${install_target_output_folder}")
set(CMAKE_INSTALL_PREFIX "${install_dir}")
set(CMAKE_INSTALL_SYSTEM_RUNTIME_DESTINATION "${install_dir}")
install(TARGETS ${output_library_name}
DESTINATION "${install_dir}"
)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/lib/${PROJECT_NAME}.jar
DESTINATION "${install_dir}"
)
endif()
# Copy any system libraries to the output folder. This really only matters on
# windows where it's good to have the visual studio runtime show up in the lib
# folder so that you don't forget to include it in your binary distribution.
INCLUDE(InstallRequiredSystemLibraries)
foreach (file_i ${CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS})
add_custom_command(TARGET ${output_library_name}
POST_BUILD
COMMAND cmake -E copy ${file_i} "${CMAKE_CURRENT_BINARY_DIR}/lib/"
)
endforeach()
This diff is collapsed.
# build the jar and shared library of C++ code needed by the JVM
mkdir build
cd build
cmake ..
cmake --build . --config Release --target install
cd ..
# setup paths so the JVM can find our jar and shared library.
export LD_LIBRARY_PATH=.
export DYLD_LIBRARY_PATH=.
export CLASSPATH=myproject.jar:.
# Now compile and run our java test that calls our C++ code.
javac swig_test.java
java swig_test
#ifndef EXAMPLE_SWIG_ApI_H_
#define EXAMPLE_SWIG_ApI_H_
// This file is essentially a small unit test for the swig cmake scripts and the jvector
// classes. All it does it define a few simple functions for writing to and summing
// arrays. The swig_test.java file then calls these C++ functions and checks if they work
// correctly.
// Let's use the jvector, a tool for efficiently binding java native arrays to C++ function
// arguments. You do this by putting this pair of include statements in your swig_api.h
// file. Then after that you can use the jvector and jvector_crit classes.
#include "jvector.h"
#ifdef SWIG
%include "jvector.h"
#endif
// ----------------------------------------------------------------------------------------
// SWIG can't expose templated functions to java. We declare these here as helper
// functions to make the non-templated routines swig will expose easier to write. You can
// see these java exposed methods below (i.e. sum(), sum_crit(), assign(), and
// assign_crit()).
template <typename T>
T tsum(const jvector_crit<T>& arr)
{
T s = 0;
for (auto& v : arr)
s += v;
return s;
}
template <typename T>
T tsum(const jvector<T>& arr)
{
T s = 0;
for (auto& v : arr)
s += v;
return s;
}
template <typename T>
void tassign(T& arr)
{
for (size_t i = 0; i < arr.size(); ++i)
arr[i] = i;
}
// ----------------------------------------------------------------------------------------
// Now write some functions SWIG will expose to java. SWIG will automatically expose
// pretty much any non-template C++ code to java. So just by defining these functions here
// we expose them to java.
//
// All global C++ functions will appear in java as static member functions of class called
// "global", which is where these sum and assign routines will appear. You can see
// examples of java code that calls them in swig_test.java.
inline int sum_crit(const jvector_crit<int16_t>& arr) { return tsum(arr); }
inline int sum(const jvector<int16_t>& arr) { return tsum(arr); }
inline void assign_crit(jvector_crit<int16_t>& arr) { tassign(arr); }
inline void assign(jvector<int16_t>& arr) { tassign(arr); }
inline int sum_crit(const jvector_crit<int32_t>& arr) { return tsum(arr); }
inline int sum(const jvector<int32_t>& arr) { return tsum(arr); }
inline void assign_crit(jvector_crit<int32_t>& arr) { tassign(arr); }
inline void assign(jvector<int32_t>& arr) { tassign(arr); }
inline int sum_crit(const jvector_crit<int64_t>& arr) { return tsum(arr); }
inline int sum(const jvector<int64_t>& arr) { return tsum(arr); }
inline void assign_crit(jvector_crit<int64_t>& arr) { tassign(arr); }
inline void assign(jvector<int64_t>& arr) { tassign(arr); }
inline int sum_crit(const jvector_crit<char>& arr) { return tsum(arr); }
inline int sum(const jvector<char>& arr) { return tsum(arr); }
inline void assign_crit(jvector_crit<char>& arr) { tassign(arr); }
inline void assign(jvector<char>& arr) { tassign(arr); }
inline double sum_crit(const jvector_crit<double>& arr) { return tsum(arr); }
inline double sum(const jvector<double>& arr) { return tsum(arr); }
inline void assign_crit(jvector_crit<double>& arr) { tassign(arr); }
inline void assign(jvector<double>& arr) { tassign(arr); }
inline float sum_crit(const jvector_crit<float>& arr) { return tsum(arr); }
inline float sum(const jvector<float>& arr) { return tsum(arr); }
inline void assign_crit(jvector_crit<float>& arr) { tassign(arr); }
inline void assign(jvector<float>& arr) { tassign(arr); }
// ----------------------------------------------------------------------------------------
#endif // EXAMPLE_SWIG_ApI_H_
/*
This file tests all the ways of using jvector and jvector_crit.
*/
import net.dlib.*;
public class swig_test
{
public static int sum(long[] arr)
{
int s = 0;
for (int i = 0; i < arr.length; ++i)
s += arr[i];
return s;
}
public static void zero(long[] arr)
{
for (int i = 0; i < arr.length; ++i)
arr[i] = 0;
}
public static int sum(byte[] arr)
{
int s = 0;
for (int i = 0; i < arr.length; ++i)
s += arr[i];
return s;
}
public static void zero(byte[] arr)
{
for (int i = 0; i < arr.length; ++i)
arr[i] = 0;
}
public static int sum(short[] arr)
{
int s = 0;
for (int i = 0; i < arr.length; ++i)
s += arr[i];
return s;
}
public static void zero(short[] arr)
{
for (int i = 0; i < arr.length; ++i)
arr[i] = 0;
}
public static int sum(int[] arr)
{
int s = 0;
for (int i = 0; i < arr.length; ++i)
s += arr[i];
return s;
}
public static void zero(int[] arr)
{
for (int i = 0; i < arr.length; ++i)
arr[i] = 0;
}
public static void assertIs28(int val)
{
if (val != 28)
{
throw new RuntimeException("Test failed " + val);
}
}
public static double sum(double[] arr)
{
double s = 0;
for (int i = 0; i < arr.length; ++i)
s += arr[i];
return s;
}
public static void zero(double[] arr)
{
for (int i = 0; i < arr.length; ++i)
arr[i] = 0;
}
public static void assertIs28(double val)
{
if (val != 28)
{
throw new RuntimeException("Test failed " + val);
}
}
public static float sum(float[] arr)
{
float s = 0;
for (int i = 0; i < arr.length; ++i)
s += arr[i];
return s;
}
public static void zero(float[] arr)
{
for (int i = 0; i < arr.length; ++i)
arr[i] = 0;
}
public static void assertIs28(float val)
{
if (val != 28)
{
throw new RuntimeException("Test failed " + val);
}
}
public static void main(String[] args)
{
{
float[] arr = new float[8];
for (int round = 0; round < 100; ++round)
{
zero(arr); global.assign(arr);
assertIs28(sum(arr));
zero(arr); global.assign_crit(arr);
assertIs28(sum(arr));
}
for (int round = 0; round < 100; ++round)
{
zero(arr); global.assign(arr);
assertIs28(sum(arr));
assertIs28(global.sum(arr));
zero(arr); global.assign_crit(arr);
assertIs28(sum(arr));
assertIs28(global.sum_crit(arr));
}
}
{
double[] arr = new double[8];
for (int round = 0; round < 100; ++round)
{
zero(arr); global.assign(arr);
assertIs28(sum(arr));
zero(arr); global.assign_crit(arr);
assertIs28(sum(arr));
}
for (int round = 0; round < 100; ++round)
{
zero(arr); global.assign(arr);
assertIs28(sum(arr));
assertIs28(global.sum(arr));
zero(arr); global.assign_crit(arr);
assertIs28(sum(arr));
assertIs28(global.sum_crit(arr));
}
}
{
byte[] arr = new byte[8];
for (int round = 0; round < 100; ++round)
{
zero(arr); global.assign(arr);
assertIs28(sum(arr));
zero(arr); global.assign_crit(arr);
assertIs28(sum(arr));
}
for (int round = 0; round < 100; ++round)
{
zero(arr); global.assign(arr);
assertIs28(sum(arr));
assertIs28(global.sum(arr));
zero(arr); global.assign_crit(arr);
assertIs28(sum(arr));
assertIs28(global.sum_crit(arr));
}
}
{
long[] arr = new long[8];
for (int round = 0; round < 100; ++round)
{
zero(arr); global.assign(arr);
assertIs28(sum(arr));
zero(arr); global.assign_crit(arr);
assertIs28(sum(arr));
}
for (int round = 0; round < 100; ++round)
{
zero(arr); global.assign(arr);
assertIs28(sum(arr));
assertIs28(global.sum(arr));
zero(arr); global.assign_crit(arr);
assertIs28(sum(arr));
assertIs28(global.sum_crit(arr));
}
}
{
short[] arr = new short[8];
for (int round = 0; round < 100; ++round)
{
zero(arr); global.assign(arr);
assertIs28(sum(arr));
zero(arr); global.assign_crit(arr);
assertIs28(sum(arr));
}
for (int round = 0; round < 100; ++round)
{
zero(arr); global.assign(arr);
assertIs28(sum(arr));
assertIs28(global.sum(arr));
zero(arr); global.assign_crit(arr);
assertIs28(sum(arr));
assertIs28(global.sum_crit(arr));
}
}
{
int[] arr = new int[8];
for (int round = 0; round < 100; ++round)
{
zero(arr); global.assign(arr);
assertIs28(sum(arr));
zero(arr); global.assign_crit(arr);
assertIs28(sum(arr));
}
for (int round = 0; round < 100; ++round)
{
zero(arr); global.assign(arr);
assertIs28(sum(arr));
assertIs28(global.sum(arr));
zero(arr); global.assign_crit(arr);
assertIs28(sum(arr));
assertIs28(global.sum_crit(arr));
}
}
System.out.println("\n\n ALL TESTS COMPLETED SUCCESSFULLY\n");
}
}
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