diff --git a/Help/release/dev/java-updates.rst b/Help/release/dev/java-updates.rst index b777807e0..8fd4ed69e 100644 --- a/Help/release/dev/java-updates.rst +++ b/Help/release/dev/java-updates.rst @@ -11,3 +11,6 @@ java-updates * The :module:`UseJava` module ``install_jar`` function learned new ``DESTINATION`` and ``COMPONENT`` options to specify the corresponding :command:`install` command options. + +* The :module:`UseJava` module gained a new ``create_javah`` + function to create C headers from Java classes. diff --git a/Modules/UseJava.cmake b/Modules/UseJava.cmake index c61591d4d..dced6eca8 100644 --- a/Modules/UseJava.cmake +++ b/Modules/UseJava.cmake @@ -309,6 +309,65 @@ # # # if you don't set the INSTALLPATH. +# +# :: +# +# create_javah(TARGET +# GENERATED_FILES +# CLASSES ... +# [CLASSPATH ...] +# [DEPENDS ...] +# [OUTPUT_NAME |OUTPUT_DIR ] +# ) +# +# Create C header files from java classes. These files provide the connective glue +# that allow your Java and C code to interact. +# +# There are two main signatures for create_javah. The first signature +# returns generated files throught variable specified by GENERATED_FILES option: +# +# :: +# +# Example: +# Create_javah(GENERATED_FILES files_headers +# CLASSES org.cmake.HelloWorld +# CLASSPATH hello.jar +# ) +# +# +# +# The second signature for create_javah creates a target which encapsulates +# header files generation. +# +# :: +# +# Example: +# Create_javah(TARGET target_headers +# CLASSES org.cmake.HelloWorld +# CLASSPATH hello.jar +# ) +# +# +# +# Both signatures share same options. +# +# ``CLASSES ...`` +# Specifies Java classes used to generate headers. +# +# ``CLASSPATH ...`` +# Specifies various paths to look up classes. Here .class files, jar files or targets +# created by command add_jar can be used. +# +# ``DEPENDS ...`` +# Targets on which the javah target depends +# +# ``OUTPUT_NAME `` +# Concatenates the resulting header files for all the classes listed by option CLASSES +# into . Same behavior as option '-o' of javah tool. +# +# ``OUTPUT_DIR `` +# Sets the directory where the header files will be generated. Same behavior as option +# '-d' of javah tool. If not specified, ${CMAKE_CURRENT_BINARY_DIR} is used as output directory. #============================================================================= # Copyright 2013 OpenGamma Ltd. @@ -1131,3 +1190,101 @@ function(create_javadoc _target) DESTINATION ${_javadoc_installpath} ) endfunction() + +function (create_javah) + cmake_parse_arguments(_create_javah + "" + "TARGET;GENERATED_FILES;OUTPUT_NAME;OUTPUT_DIR" + "CLASSES;CLASSPATH;DEPENDS" + ${ARGN}) + + # ckeck parameters + if (NOT _create_javah_TARGET AND NOT _create_javah_GENERATED_FILES) + message (FATAL_ERROR "create_javah: TARGET or GENERATED_FILES must be specified.") + endif() + if (_create_javah_OUTPUT_NAME AND _create_javah_OUTPUT_DIR) + message (FATAL_ERROR "create_javah: OUTPUT_NAME and OUTPUT_DIR are mutually exclusive.") + endif() + + if (NOT _create_javah_CLASSES) + message (FATAL_ERROR "create_javah: CLASSES is a required parameter.") + endif() + + set (_output_files) + if (WIN32 AND NOT CYGWIN AND CMAKE_HOST_SYSTEM_NAME MATCHES "Windows") + set(_classpath_sep ";") + else () + set(_classpath_sep ":") + endif() + + # handle javah options + set (_javah_options) + + if (_create_javah_CLASSPATH) + # CLASSPATH can specify directories, jar files or targets created with add_jar command + set (_classpath) + foreach (_path IN LISTS _create_javah_CLASSPATH) + if (TARGET ${_path}) + get_target_property (_jar_path ${_path} JAR_FILE) + if (_jar_path) + list (APPEND _classpath "${_jar_path}") + list (APPEND _create_javah_DEPENDS "${_path}") + else() + message(SEND_ERROR "create_javah: CLASSPATH target ${_path} is not a jar.") + endif() + elseif (EXISTS "${_path}") + list (APPEND _classpath "${_path}") + if (NOT IS_DIRECTORY "${_path}") + list (APPEND _create_javah_DEPENDS "${_path}") + endif() + else() + message(SEND_ERROR "create_javah: CLASSPATH entry ${_path} does not exist.") + endif() + endforeach() + string (REPLACE ";" "${_classpath_sep}" _classpath "${_classpath}") + list (APPEND _javah_options -classpath ${_classpath}) + endif() + + if (_create_javah_OUTPUT_DIR) + list (APPEND _javah_options -d "${_create_javah_OUTPUT_DIR}") + endif() + + if (_create_javah_OUTPUT_NAME) + list (APPEND _javah_options -o "${_create_javah_OUTPUT_NAME}") + set (_output_files "${_create_javah_OUTPUT_NAME}") + + get_filename_component (_create_javah_OUTPUT_DIR "${_create_javah_OUTPUT_NAME}" DIRECTORY) + get_filename_component (_create_javah_OUTPUT_DIR "${_create_javah_OUTPUT_DIR}" ABSOLUTE) + endif() + + if (NOT _create_javah_OUTPUT_DIR) + set (_create_javah_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}") + endif() + + if (NOT _create_javah_OUTPUT_NAME) + # compute output names + foreach (_class IN LISTS _create_javah_CLASSES) + string (REPLACE "." "_" _c_header "${_class}") + set (_c_header "${_create_javah_OUTPUT_DIR}/${_c_header}.h") + list (APPEND _output_files "${_c_header}") + endforeach() + endif() + + # finalize custom command arguments + if (_create_javah_DEPENDS) + list (INSERT _create_javah_DEPENDS 0 DEPENDS) + endif() + + add_custom_command (OUTPUT ${_output_files} + COMMAND "${Java_JAVAH_EXECUTABLE}" ${_javah_options} -jni ${_create_javah_CLASSES} + ${_create_javah_DEPENDS} + WORKING_DIRECTORY ${_create_javah_OUTPUT_DIR} + COMMENT "Building C header files from classes...") + + if (_create_javah_TARGET) + add_custom_target (${_create_javah_TARGET} ALL DEPENDS ${_output_files}) + endif() + if (_create_javah_GENERATED_FILES) + set (${_create_javah_GENERATED_FILES} ${_output_files} PARENT_SCOPE) + endif() +endfunction() diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index 35b29bf6e..801d7e83a 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -3036,6 +3036,31 @@ ${CMake_BINARY_DIR}/bin/cmake -DDIR=dev -P ${CMake_SOURCE_DIR}/Utilities/Release --build-options ${build_options} --test-command ${JAVA_RUNTIME} -classpath hello2.jar HelloWorld) list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/JavaJarSourceList") + + # For next test, java tool must have same architecture as toolchain + math(EXPR _object_mode "${CMAKE_SIZEOF_VOID_P} * 8") + execute_process( + COMMAND "${Java_JAVA_EXECUTABLE}" -d${_object_mode} -version + OUTPUT_QUIET ERROR_QUIET RESULT_VARIABLE _result + ) + if(_result EQUAL 0) + if(CMAKE_CONFIGURATION_TYPES) + set (JAVAH_LIBRARY_PATH ${CMake_BINARY_DIR}/Tests/JavaJavah/$) + else() + set (JAVAH_LIBRARY_PATH ${CMake_BINARY_DIR}/Tests/JavaJavah) + endif() + add_test(NAME Java.Javah COMMAND ${CMAKE_CTEST_COMMAND} + --build-and-test + "${CMake_SOURCE_DIR}/Tests/JavaJavah" + "${CMake_BINARY_DIR}/Tests/JavaJavah" + ${build_generator_args} + --build-project helloJavah + --build-two-config + --build-run-dir "${CMake_BINARY_DIR}/Tests/JavaJavah/" + --build-options ${build_options} + --test-command ${JAVA_RUNTIME} -Djava.library.path=${JAVAH_LIBRARY_PATH} -classpath hello3.jar HelloWorld2) + list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/JavaJavah") + endif() endif() endif() endif() diff --git a/Tests/JavaJavah/B.cpp b/Tests/JavaJavah/B.cpp new file mode 100644 index 000000000..266675703 --- /dev/null +++ b/Tests/JavaJavah/B.cpp @@ -0,0 +1,10 @@ + +#include +#include + +#include "B.h" + +JNIEXPORT void JNICALL Java_B_printName(JNIEnv *, jobject) +{ + printf("B\n"); +} diff --git a/Tests/JavaJavah/B.java b/Tests/JavaJavah/B.java new file mode 100644 index 000000000..d731f396c --- /dev/null +++ b/Tests/JavaJavah/B.java @@ -0,0 +1,19 @@ +class B +{ + public B() + { + } + + public native void printName(); + + static { + try { + + System.loadLibrary("B"); + + } catch (UnsatisfiedLinkError e) { + System.err.println("Native code library failed to load.\n" + e); + System.exit(1); + } + } +} diff --git a/Tests/JavaJavah/CMakeLists.txt b/Tests/JavaJavah/CMakeLists.txt new file mode 100644 index 000000000..83b0ad06a --- /dev/null +++ b/Tests/JavaJavah/CMakeLists.txt @@ -0,0 +1,20 @@ +project(helloJavah Java CXX) + +cmake_minimum_required (VERSION 2.6) +set(CMAKE_VERBOSE_MAKEFILE 1) + +find_package(Java COMPONENTS Development) +include (UseJava) + +# JNI support +find_package(JNI) + +add_jar(hello3 B.java HelloWorld2.java) +create_javah(TARGET B_javah CLASSES B CLASSPATH hello3) + +add_library(B SHARED B.cpp) +add_dependencies(B B_javah) + +target_include_directories(B PRIVATE ${CMAKE_CURRENT_BINARY_DIR} + ${JAVA_INCLUDE_PATH} + ${JAVA_INCLUDE_PATH2}) diff --git a/Tests/JavaJavah/HelloWorld2.java b/Tests/JavaJavah/HelloWorld2.java new file mode 100644 index 000000000..faf7277fe --- /dev/null +++ b/Tests/JavaJavah/HelloWorld2.java @@ -0,0 +1,10 @@ +class HelloWorld2 +{ + public static void main(String args[]) + { + B b; + b = new B(); + b.printName(); + System.out.println("Hello World!"); + } +}