1 # ---------------------------------------------------------------------------- 2 # CMake file for java support 3 # ---------------------------------------------------------------------------- 4 if(IOS OR WINRT OR NOT PYTHON_DEFAULT_AVAILABLE OR NOT ANT_EXECUTABLE OR NOT (JNI_FOUND OR (ANDROID AND ANDROID_NATIVE_API_LEVEL GREATER 7)) 5 OR BUILD_opencv_world 6 ) 7 ocv_module_disable(java) 8 endif() 9 10 set(the_description "The java bindings") 11 ocv_add_module(java BINDINGS opencv_core opencv_imgproc) 12 ocv_module_include_directories("${CMAKE_CURRENT_SOURCE_DIR}/generator/src/cpp") 13 ocv_module_include_directories("${OpenCV_SOURCE_DIR}/include") 14 15 if(NOT ANDROID) 16 include_directories(${JNI_INCLUDE_DIRS}) 17 endif() 18 19 # output locations 20 set(JAVA_INSTALL_ROOT "sdk/java") 21 set(JNI_INSTALL_ROOT "sdk/native") 22 23 # get list of modules to wrap 24 # message(STATUS "Wrapped in java:") 25 set(OPENCV_JAVA_MODULES) 26 foreach(m ${OPENCV_MODULES_BUILD}) 27 if (";${OPENCV_MODULE_${m}_WRAPPERS};" MATCHES ";java;" AND HAVE_${m}) 28 string(REPLACE "opencv_" "" m ${m}) 29 list(APPEND OPENCV_JAVA_MODULES ${m}) 30 # message(STATUS "\topencv_${m}") 31 endif() 32 endforeach() 33 34 ###################################################################################################################################### 35 36 # UTILITY: make C headers go first 37 macro(sort_headers_c_cpp __lst) 38 set(__cpp ${${__lst}}) 39 ocv_list_filterout(__cpp "\\\\.h$") 40 if(__cpp) 41 list(REMOVE_ITEM ${__lst} ${__cpp}) 42 list(APPEND ${__lst} ${__cpp}) 43 endif() 44 unset(__cpp) 45 endmacro() 46 47 # UTILITY: force cmake to rerun when files from list have changed 48 macro(add_cmake_dependencies) 49 foreach (f ${ARGN}) 50 get_filename_component(f_name "${f}" NAME) 51 configure_file(${f} ${OpenCV_BINARY_DIR}/junk/${f_name}.junk COPYONLY) 52 endforeach() 53 endmacro() 54 55 # UTILITY: glob specific sources and append them to list (type is in H, CPP, JAVA, AIDL) 56 macro(glob_more_specific_sources _type _root _output) 57 unset(_masks) 58 if(${_type} STREQUAL "H") 59 set(_masks "${_root}/src/cpp/*.h" "${root}/src/cpp/*.hpp") 60 elseif(${_type} STREQUAL "CPP") 61 set(_masks "${_root}/src/cpp/*.cpp") 62 elseif(${_type} STREQUAL "JAVA") 63 set(_masks "${_root}/src/java/*.java") 64 elseif(${_type} STREQUAL "AIDL") 65 set(_masks "${_root}/src/java/*.aidl") 66 endif() 67 if (_masks) 68 file(GLOB _result ${_masks}) 69 list(APPEND ${_output} ${_result}) 70 else() 71 message(WARNING "Bad argument passed to macro: skipped") 72 endif() 73 endmacro() 74 75 # UTILITY: copy common java test files and add them to _deps 76 # copy_common_tests(<source-folder> <destination-folder> <variable-to-store-deps>) 77 macro(copy_common_tests _src_location _dst_location _deps) 78 set(_src ${${_src_location}}) 79 set(_dst ${${_dst_location}}) 80 file(GLOB_RECURSE _files RELATIVE "${_src}" "${_src}/res/*" "${_src}/src/*") 81 foreach(f ${_files}) 82 add_custom_command( 83 OUTPUT "${_dst}/${f}" 84 COMMAND ${CMAKE_COMMAND} -E copy "${_src}/${f}" "${_dst}/${f}" 85 MAIN_DEPENDENCY "${_src}/${f}" 86 COMMENT "Copying ${f}") 87 list(APPEND ${_deps} "${_src}/${f}" "${_dst}/${f}") 88 endforeach() 89 unset(_files) 90 unset(_src) 91 unset(_dst) 92 endmacro() 93 94 # UTILITY: copy all java tests for specific module and add them to _deps 95 # copy_modules_tests(<modules-list> <destination-folder> <variable-to-store-deps>) 96 macro(copy_modules_tests _modules _dst_location _deps) 97 set(_dst ${${_dst_location}}) 98 foreach(module ${${_modules}}) 99 set(_src "${OPENCV_MODULE_opencv_${module}_LOCATION}/misc/java/test") 100 set(_tree "src/org/opencv/test/${module}") 101 file(GLOB _files RELATIVE "${_src}" "${_src}/*.java") 102 foreach (f ${_files}) 103 add_custom_command( 104 OUTPUT "${_dst}/${_tree}/${f}" 105 COMMAND ${CMAKE_COMMAND} -E copy "${_src}/${f}" "${_dst}/${_tree}/${f}" 106 MAIN_DEPENDENCY "${_src}/${f}" 107 COMMENT "Copying ${f}") 108 list(APPEND ${_deps} "${_src}/${f}" "${_dst}/${_tree}/${f}") 109 endforeach() 110 unset(_files) 111 unset(_src) 112 unset(_tree) 113 endforeach() 114 unset(_dst) 115 endmacro() 116 117 ###################################################################################################################################### 118 119 # scripts 120 set(scripts_gen_java "${CMAKE_CURRENT_SOURCE_DIR}/generator/gen_java.py") 121 set(scripts_hdr_parser "${CMAKE_CURRENT_SOURCE_DIR}/../python/src2/hdr_parser.py") 122 123 # directory to store temporary files generated on first gen_java.py run 124 set(probe_dir "${CMAKE_CURRENT_BINARY_DIR}/gen_java_out") 125 126 # handwritten C/C++ and Java sources 127 glob_more_specific_sources(H "${CMAKE_CURRENT_SOURCE_DIR}/generator" handwritten_h_sources) 128 glob_more_specific_sources(CPP "${CMAKE_CURRENT_SOURCE_DIR}/generator" handwritten_cpp_sources) 129 glob_more_specific_sources(JAVA "${CMAKE_CURRENT_SOURCE_DIR}/generator" handwritten_java_sources) 130 glob_more_specific_sources(AIDL "${CMAKE_CURRENT_SOURCE_DIR}/generator" handwritten_aidl_sources) 131 132 # headers of OpenCV modules 133 set(opencv_public_headers "") 134 set(generated_cpp_sources "") 135 set(generated_java_sources "") 136 foreach(module ${OPENCV_JAVA_MODULES}) 137 set(module_java_dir "${OPENCV_MODULE_opencv_${module}_LOCATION}/misc/java") 138 set(custom_header_list "${module_java_dir}/filelist") 139 if(EXISTS "${custom_header_list}") 140 file(STRINGS "${custom_header_list}" module_headers) 141 ocv_list_add_prefix(module_headers "${OPENCV_MODULE_opencv_${module}_LOCATION}/") 142 else() 143 set(module_headers "${OPENCV_MODULE_opencv_${module}_HEADERS}") 144 endif() 145 146 if(NOT module_headers) 147 message(WARNING "Module ${module} does not have headers to wrap for java") 148 endif() 149 150 sort_headers_c_cpp(module_headers) 151 152 set(opencv_public_headers_${module} ${module_headers}) 153 list(APPEND opencv_public_headers ${module_headers}) 154 list(APPEND generated_cpp_sources "${CMAKE_CURRENT_BINARY_DIR}/${module}.cpp") 155 156 include_directories("${module_java_dir}/src/cpp") 157 158 glob_more_specific_sources(H "${module_java_dir}" handwritten_h_sources) 159 glob_more_specific_sources(CPP "${module_java_dir}" handwritten_cpp_sources) 160 glob_more_specific_sources(JAVA "${module_java_dir}" handwritten_java_sources) 161 glob_more_specific_sources(AIDL "${module_java_dir}" handwritten_aidl_sources) 162 163 # first run of gen_java.py (to get list of generated files) 164 file(REMOVE_RECURSE "${probe_dir}") 165 file(MAKE_DIRECTORY "${probe_dir}") 166 execute_process(COMMAND ${PYTHON_DEFAULT_EXECUTABLE} "${scripts_gen_java}" "${scripts_hdr_parser}" ${module} ${opencv_public_headers_${module}} 167 WORKING_DIRECTORY "${probe_dir}" 168 OUTPUT_QUIET ERROR_QUIET) 169 file(GLOB_RECURSE generated_java_sources_${module} RELATIVE "${probe_dir}" "${probe_dir}/*.java") 170 ocv_list_add_prefix(generated_java_sources_${module} "${CMAKE_CURRENT_BINARY_DIR}/") 171 list(APPEND generated_java_sources ${generated_java_sources_${module}}) 172 endforeach() 173 174 file(REMOVE_RECURSE "${probe_dir}") 175 176 if(NOT ANDROID) 177 ocv_list_filterout(handwritten_java_sources "/(engine|android)\\\\+") 178 ocv_list_filterout(handwritten_aidl_sources "/(engine|android)\\\\+") 179 else() 180 file(GLOB_RECURSE handwrittren_lib_project_files_rel RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}/android_lib/" "${CMAKE_CURRENT_SOURCE_DIR}/android_lib/*") 181 list(REMOVE_ITEM handwrittren_lib_project_files_rel "${ANDROID_MANIFEST_FILE}") 182 endif() 183 184 # IMPORTANT: add dependencies to cmake (we should rerun cmake if any of these files is modified) 185 add_cmake_dependencies(${scripts_gen_java} ${scripts_hdr_parser} ${opencv_public_headers}) 186 187 ###################################################################################################################################### 188 189 # step 1: generate .cpp/.java from OpenCV headers 190 set(step1_depends "${scripts_gen_java}" "${scripts_hdr_parser}" ${opencv_public_headers}) 191 foreach(module ${OPENCV_JAVA_MODULES}) 192 # second run of gen_java.py (at build time) 193 add_custom_command(OUTPUT ${generated_java_sources_${module}} "${CMAKE_CURRENT_BINARY_DIR}/${module}.cpp" 194 COMMAND ${PYTHON_DEFAULT_EXECUTABLE} "${scripts_gen_java}" "${scripts_hdr_parser}" ${module} ${opencv_public_headers_${module}} 195 WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} 196 DEPENDS "${scripts_gen_java}" "${scripts_hdr_parser}" ${opencv_public_headers_${module}} 197 ) 198 endforeach() 199 200 # step 2: TODO: generate documentation somewhere 201 202 # step 3: copy files to destination 203 set(step3_input_files ${generated_java_sources} ${handwritten_java_sources} ${handwritten_aidl_sources}) 204 set(copied_files "") 205 foreach(java_file ${step3_input_files}) 206 get_filename_component(java_file_name "${java_file}" NAME) 207 string(REPLACE "-jdoc.java" ".java" java_file_name "${java_file_name}") 208 string(REPLACE "+" "/" java_file_name "${java_file_name}") 209 set(output_name "${OpenCV_BINARY_DIR}/src/org/opencv/${java_file_name}") 210 add_custom_command(OUTPUT "${output_name}" 211 COMMAND ${CMAKE_COMMAND} -E copy "${java_file}" "${output_name}" 212 MAIN_DEPENDENCY "${java_file}" 213 DEPENDS ${step1_depends} ${generated_java_sources} ${handwritten_java_sources} 214 COMMENT "Generating src/org/opencv/${java_file_name}" 215 ) 216 list(APPEND copied_files "${output_name}") 217 218 if(ANDROID) 219 get_filename_component(install_subdir "${java_file_name}" PATH) 220 install(FILES "${output_name}" DESTINATION "${JAVA_INSTALL_ROOT}/src/org/opencv/${install_subdir}" COMPONENT java) 221 endif() 222 endforeach() 223 224 if(ANDROID) 225 set(android_copied_files "") 226 set(android_step3_input_files "") 227 foreach(file ${handwrittren_lib_project_files_rel}) 228 configure_file("${CMAKE_CURRENT_SOURCE_DIR}/android_lib/${file}" "${OpenCV_BINARY_DIR}/${file}" @ONLY) 229 list(APPEND android_copied_files "${OpenCV_BINARY_DIR}/${file}") 230 list(APPEND android_step3_input_files "${CMAKE_CURRENT_SOURCE_DIR}/android_lib/${file}") 231 232 if(NOT file MATCHES "jni/.+") 233 get_filename_component(install_subdir "${file}" PATH) 234 install(FILES "${OpenCV_BINARY_DIR}/${file}" DESTINATION "${JAVA_INSTALL_ROOT}/${install_subdir}" COMPONENT java) 235 endif() 236 endforeach() 237 238 # library project jni sources (nothing really depends on them so we will not add them to step3_input_files) 239 foreach(jni_file ${handwritten_cpp_sources} ${handwritten_h_sources} ${generated_cpp_sources}) 240 get_filename_component(jni_file_name "${jni_file}" NAME) 241 add_custom_command(OUTPUT "${OpenCV_BINARY_DIR}/jni/${jni_file_name}" 242 COMMAND ${CMAKE_COMMAND} -E copy "${jni_file}" "${OpenCV_BINARY_DIR}/jni/${jni_file_name}" 243 DEPENDS "${jni_file}" ${java_hdr_deps} 244 COMMENT "Generating jni/${jni_file_name}" 245 ) 246 list(APPEND android_copied_files "${OpenCV_BINARY_DIR}/jni/${jni_file_name}") 247 endforeach() 248 endif(ANDROID) 249 250 # step 3.5: generate Android library project 251 if(ANDROID AND ANDROID_EXECUTABLE) 252 set(lib_target_files ${ANDROID_LIB_PROJECT_FILES}) 253 ocv_list_add_prefix(lib_target_files "${OpenCV_BINARY_DIR}/") 254 255 android_get_compatible_target(lib_target_sdk_target ${ANDROID_NATIVE_API_LEVEL} ${ANDROID_SDK_TARGET} 14) 256 configure_file("${CMAKE_CURRENT_SOURCE_DIR}/android_lib/${ANDROID_MANIFEST_FILE}" "${CMAKE_CURRENT_BINARY_DIR}/${ANDROID_MANIFEST_FILE}" @ONLY) 257 258 add_custom_command(OUTPUT ${lib_target_files} "${OpenCV_BINARY_DIR}/${ANDROID_MANIFEST_FILE}" 259 COMMAND ${CMAKE_COMMAND} -E remove ${lib_target_files} 260 COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_BINARY_DIR}/${ANDROID_MANIFEST_FILE}" "${OpenCV_BINARY_DIR}/${ANDROID_MANIFEST_FILE}" 261 COMMAND ${ANDROID_EXECUTABLE} --silent create lib-project --path \"${OpenCV_BINARY_DIR}\" --target \"${lib_target_sdk_target}\" --name OpenCV --package org.opencv 2>\"${CMAKE_CURRENT_BINARY_DIR}/create_lib_project.log\" 262 COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_BINARY_DIR}/${ANDROID_MANIFEST_FILE}" "${OpenCV_BINARY_DIR}/${ANDROID_MANIFEST_FILE}" 263 MAIN_DEPENDENCY "${CMAKE_CURRENT_BINARY_DIR}/${ANDROID_MANIFEST_FILE}" 264 DEPENDS ${android_step3_input_files} ${android_copied_files} 265 COMMENT "Generating OpenCV Android library project. SDK target: ${lib_target_sdk_target}" 266 ) 267 list(APPEND copied_files ${lib_target_files} "${OpenCV_BINARY_DIR}/${ANDROID_MANIFEST_FILE}") 268 list(APPEND step3_input_files "${CMAKE_CURRENT_BINARY_DIR}/${ANDROID_MANIFEST_FILE}") 269 270 install(FILES "${OpenCV_BINARY_DIR}/${ANDROID_PROJECT_PROPERTIES_FILE}" DESTINATION ${JAVA_INSTALL_ROOT} COMPONENT java) 271 install(FILES "${OpenCV_BINARY_DIR}/${ANDROID_MANIFEST_FILE}" DESTINATION ${JAVA_INSTALL_ROOT} COMPONENT java) 272 # creating empty 'gen' and 'res' folders 273 install(CODE "MAKE_DIRECTORY(\"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${JAVA_INSTALL_ROOT}/gen\")" COMPONENT java) 274 install(CODE "MAKE_DIRECTORY(\"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${JAVA_INSTALL_ROOT}/res\")" COMPONENT java) 275 endif(ANDROID AND ANDROID_EXECUTABLE) 276 277 set(step3_depends ${step2_depends} ${step3_input_files} ${copied_files}) 278 279 if(ANDROID) 280 set(LIB_NAME_SUFIX "${OPENCV_VERSION_MAJOR}") 281 else() 282 set(LIB_NAME_SUFIX "${OPENCV_VERSION_MAJOR}${OPENCV_VERSION_MINOR}${OPENCV_VERSION_PATCH}") 283 endif() 284 285 # step 4: build jar 286 if(ANDROID) 287 set(JAR_FILE "${OpenCV_BINARY_DIR}/bin/classes.jar") 288 if(ANDROID_TOOLS_Pkg_Revision GREATER 13) 289 # build the library project 290 # normally we should do this after a native part, but for a library project we can build the java part first 291 add_custom_command(OUTPUT "${JAR_FILE}" "${JAR_FILE}.dephelper" 292 COMMAND ${ANT_EXECUTABLE} -q -noinput -k debug 293 COMMAND ${CMAKE_COMMAND} -E touch "${JAR_FILE}.dephelper" # can not rely on classes.jar because different versions of SDK update timestamp at different times 294 WORKING_DIRECTORY "${OpenCV_BINARY_DIR}" 295 DEPENDS ${step3_depends} 296 COMMENT "Building OpenCV Android library project" 297 ) 298 else() 299 # ditto 300 add_custom_command(OUTPUT "${JAR_FILE}" "${JAR_FILE}.dephelper" 301 COMMAND ${CMAKE_COMMAND} -E touch "${JAR_FILE}" 302 COMMAND ${CMAKE_COMMAND} -E touch "${JAR_FILE}.dephelper" 303 WORKING_DIRECTORY "${OpenCV_BINARY_DIR}" 304 DEPENDS ${step3_depends} 305 COMMENT "" 306 ) 307 endif() 308 else(ANDROID) 309 set(JAR_NAME opencv-${LIB_NAME_SUFIX}.jar) 310 set(JAR_FILE "${OpenCV_BINARY_DIR}/bin/${JAR_NAME}") 311 configure_file("${CMAKE_CURRENT_SOURCE_DIR}/build.xml.in" "${OpenCV_BINARY_DIR}/build.xml" @ONLY) 312 list(APPEND step3_depends "${OpenCV_BINARY_DIR}/build.xml") 313 314 add_custom_command(OUTPUT "${JAR_FILE}" "${JAR_FILE}.dephelper" 315 COMMAND ${ANT_EXECUTABLE} -q -noinput -k jar 316 COMMAND ${CMAKE_COMMAND} -E touch "${JAR_FILE}.dephelper" 317 WORKING_DIRECTORY "${OpenCV_BINARY_DIR}" 318 DEPENDS ${step3_depends} 319 COMMENT "Generating ${JAR_NAME}" 320 ) 321 322 install(FILES ${JAR_FILE} OPTIONAL DESTINATION ${OPENCV_JAR_INSTALL_PATH} COMPONENT java) 323 endif(ANDROID) 324 325 # step 5: build native part 326 327 # workarounding lack of `__attribute__ ((visibility("default")))` in jni_md.h/JNIEXPORT 328 string(REPLACE "-fvisibility=hidden" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") 329 330 ocv_add_library(${the_module} SHARED ${handwritten_h_sources} ${handwritten_cpp_sources} ${generated_cpp_sources} 331 ${copied_files} 332 "${JAR_FILE}" "${JAR_FILE}.dephelper") 333 334 if(BUILD_FAT_JAVA_LIB) 335 set(__deps ${OPENCV_MODULE_${the_module}_DEPS} ${OPENCV_MODULES_BUILD}) 336 foreach(m ${OPENCV_MODULES_BUILD}) # filterout INTERNAL (like opencv_ts) and BINDINGS (like opencv_python) modules 337 ocv_assert(DEFINED OPENCV_MODULE_${m}_CLASS) 338 if(NOT OPENCV_MODULE_${m}_CLASS STREQUAL "PUBLIC") 339 list(REMOVE_ITEM __deps ${m}) 340 endif() 341 endforeach() 342 ocv_list_unique(__deps) 343 set(__extradeps ${__deps}) 344 ocv_list_filterout(__extradeps "^opencv_") 345 if(__extradeps) 346 list(REMOVE_ITEM __deps ${__extradeps}) 347 endif() 348 if(APPLE) 349 foreach(_dep ${__deps}) 350 ocv_target_link_libraries(${the_module} -Wl,-force_load "${_dep}") 351 endforeach() 352 else() 353 ocv_target_link_libraries(${the_module} -Wl,-whole-archive ${__deps} -Wl,-no-whole-archive) 354 endif() 355 ocv_target_link_libraries(${the_module} ${__extradeps} ${OPENCV_LINKER_LIBS}) 356 else() 357 ocv_target_link_libraries(${the_module} ${OPENCV_MODULE_${the_module}_DEPS} ${OPENCV_LINKER_LIBS}) 358 endif() 359 360 # Additional target properties 361 set_target_properties(${the_module} PROPERTIES 362 OUTPUT_NAME "${the_module}${LIB_NAME_SUFIX}" 363 ARCHIVE_OUTPUT_DIRECTORY ${LIBRARY_OUTPUT_PATH} 364 LIBRARY_OUTPUT_DIRECTORY ${LIBRARY_OUTPUT_PATH} 365 RUNTIME_OUTPUT_DIRECTORY ${EXECUTABLE_OUTPUT_PATH} 366 INSTALL_NAME_DIR ${OPENCV_LIB_INSTALL_PATH} 367 LINK_INTERFACE_LIBRARIES "" 368 ) 369 370 if(ANDROID) 371 ocv_target_link_libraries(${the_module} jnigraphics) # for Mat <=> Bitmap converters 372 373 # force strip library after the build command 374 # because samples and tests will make a copy of the library before install 375 get_target_property(__opencv_java_location ${the_module} LOCATION) 376 # Turn off stripping in debug build 377 if ( NOT (CMAKE_BUILD_TYPE MATCHES "Debug")) 378 add_custom_command(TARGET ${the_module} POST_BUILD COMMAND ${CMAKE_STRIP} --strip-unneeded "${__opencv_java_location}") 379 endif() 380 endif() 381 382 if(WIN32) 383 set_target_properties(${the_module} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) 384 endif() 385 386 if(ENABLE_SOLUTION_FOLDERS) 387 set_target_properties(${the_module} PROPERTIES FOLDER "bindings") 388 endif() 389 390 if(ANDROID) 391 ocv_install_target(${the_module} OPTIONAL EXPORT OpenCVModules 392 LIBRARY DESTINATION ${OPENCV_LIB_INSTALL_PATH} COMPONENT java 393 ARCHIVE DESTINATION ${OPENCV_LIB_INSTALL_PATH} COMPONENT java) 394 else() 395 if(NOT INSTALL_CREATE_DISTRIB) 396 ocv_install_target(${the_module} OPTIONAL EXPORT OpenCVModules 397 RUNTIME DESTINATION ${OPENCV_JAR_INSTALL_PATH} COMPONENT java 398 LIBRARY DESTINATION ${OPENCV_JAR_INSTALL_PATH} COMPONENT java) 399 else() 400 ocv_install_target(${the_module} OPTIONAL EXPORT OpenCVModules 401 RUNTIME DESTINATION ${OPENCV_JAR_INSTALL_PATH}/${OpenCV_ARCH} COMPONENT java 402 LIBRARY DESTINATION ${OPENCV_JAR_INSTALL_PATH}/${OpenCV_ARCH} COMPONENT java) 403 endif() 404 endif() 405 406 ###################################################################################################################################### 407 408 if(BUILD_TESTS) 409 if(ANDROID) 410 add_subdirectory(android_test) 411 else() 412 add_subdirectory(pure_test) 413 endif() 414 endif() 415