OpenCVDetectAndroidSDK.cmake 15.8 KB
Newer Older
1 2 3 4
if(EXISTS "${ANDROID_EXECUTABLE}")
  set(ANDROID_SDK_DETECT_QUIET TRUE)
endif()

A
Andrey Kamaev 已提交
5
file(TO_CMAKE_PATH "$ENV{ProgramFiles}" ProgramFiles_ENV_PATH)
6
file(TO_CMAKE_PATH "$ENV{HOME}" HOME_ENV_PATH)
A
Andrey Kamaev 已提交
7

8 9 10
if(CMAKE_HOST_WIN32)
  set(ANDROID_SDK_OS windows)
elseif(CMAKE_HOST_APPLE)
11
  set(ANDROID_SDK_OS macosx)
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
else()
  set(ANDROID_SDK_OS linux)
endif()

#find android SDK: search in ANDROID_SDK first
find_host_program(ANDROID_EXECUTABLE
  NAMES android.bat android
  PATH_SUFFIXES tools
  PATHS
    ENV ANDROID_SDK
  DOC "Android SDK location"
  NO_DEFAULT_PATH
  )

# Now search default paths
A
Andrey Kamaev 已提交
27 28
find_host_program(ANDROID_EXECUTABLE
  NAMES android.bat android
29 30 31 32 33 34 35 36 37
  PATH_SUFFIXES android-sdk-${ANDROID_SDK_OS}/tools
                android-sdk-${ANDROID_SDK_OS}_x86/tools
                android-sdk-${ANDROID_SDK_OS}_86/tools
                android-sdk/tools
  PATHS /opt
        "${HOME_ENV_PATH}/NVPACK"
        "$ENV{SystemDrive}/NVPACK"
        "${ProgramFiles_ENV_PATH}/Android"
  DOC "Android SDK location"
A
Andrey Kamaev 已提交
38 39 40
  )

if(ANDROID_EXECUTABLE)
41
  if(NOT ANDROID_SDK_DETECT_QUIET)
V
Vadim Pisarevsky 已提交
42
    message(STATUS "Found android tool: ${ANDROID_EXECUTABLE}")
43 44
  endif()

A
Andrey Kamaev 已提交
45 46 47 48 49 50 51 52 53 54 55
  get_filename_component(ANDROID_SDK_TOOLS_PATH "${ANDROID_EXECUTABLE}" PATH)

  #read source.properties
  if(EXISTS "${ANDROID_SDK_TOOLS_PATH}/source.properties")
    file(STRINGS "${ANDROID_SDK_TOOLS_PATH}/source.properties" ANDROID_SDK_TOOLS_SOURCE_PROPERTIES_LINES REGEX "^[ ]*[^#].*$")
    foreach(line ${ANDROID_SDK_TOOLS_SOURCE_PROPERTIES_LINES})
      string(REPLACE "\\:" ":" line ${line})
      string(REPLACE "=" ";" line ${line})
      list(GET line 0 line_name)
      list(GET line 1 line_value)
      string(REPLACE "." "_" line_name ${line_name})
56
      SET(ANDROID_TOOLS_${line_name} "${line_value}" CACHE INTERNAL "from ${ANDROID_SDK_TOOLS_PATH}/source.properties")
A
Andrey Kamaev 已提交
57 58 59 60
      MARK_AS_ADVANCED(ANDROID_TOOLS_${line_name})
    endforeach()
  endif()

61
  #fix missing revision (SDK tools before r9 don't set revision number correctly)
A
Andrey Kamaev 已提交
62
  if(NOT ANDROID_TOOLS_Pkg_Revision)
63
    SET(ANDROID_TOOLS_Pkg_Revision "Unknown" CACHE INTERNAL "")
A
Andrey Kamaev 已提交
64 65 66
    MARK_AS_ADVANCED(ANDROID_TOOLS_Pkg_Revision)
  endif()

67
  #fix missing description
A
Andrey Kamaev 已提交
68
  if(NOT ANDROID_TOOLS_Pkg_Desc)
69
    SET(ANDROID_TOOLS_Pkg_Desc "Android SDK Tools, revision ${ANDROID_TOOLS_Pkg_Revision}." CACHE INTERNAL "")
A
Andrey Kamaev 已提交
70 71 72
    MARK_AS_ADVANCED(ANDROID_TOOLS_Pkg_Desc)
  endif()

73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
  #warn about outdated SDK
  if(NOT ANDROID_TOOLS_Pkg_Revision GREATER 13)
    SET(ANDROID_TOOLS_Pkg_Desc "${ANDROID_TOOLS_Pkg_Desc} It is recommended to update your SDK tools to revision 14 or newer." CACHE INTERNAL "")
  endif()

  if(ANDROID_TOOLS_Pkg_Revision GREATER 13)
    SET(ANDROID_PROJECT_PROPERTIES_FILE project.properties)
    SET(ANDROID_ANT_PROPERTIES_FILE ant.properties)
  else()
    SET(ANDROID_PROJECT_PROPERTIES_FILE default.properties)
    SET(ANDROID_ANT_PROPERTIES_FILE build.properties)
  endif()

  set(ANDROID_MANIFEST_FILE AndroidManifest.xml)
  set(ANDROID_LIB_PROJECT_FILES build.xml local.properties proguard-project.txt ${ANDROID_PROJECT_PROPERTIES_FILE})
88
  set(ANDROID_PROJECT_FILES ${ANDROID_LIB_PROJECT_FILES})
89

A
Andrey Kamaev 已提交
90
  #get installed targets
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
  if(ANDROID_TOOLS_Pkg_Revision GREATER 11)
    execute_process(COMMAND ${ANDROID_EXECUTABLE} list target -c
      RESULT_VARIABLE ANDROID_PROCESS
      OUTPUT_VARIABLE ANDROID_SDK_TARGETS
      ERROR_VARIABLE ANDROID_PROCESS_ERRORS
      OUTPUT_STRIP_TRAILING_WHITESPACE
      )
    string(REGEX MATCHALL "[^\n]+" ANDROID_SDK_TARGETS "${ANDROID_SDK_TARGETS}")
  else()
    #old SDKs (r11 and older) don't provide compact list
    execute_process(COMMAND ${ANDROID_EXECUTABLE} list target
      RESULT_VARIABLE ANDROID_PROCESS
      OUTPUT_VARIABLE ANDROID_SDK_TARGETS_FULL
      ERROR_VARIABLE ANDROID_PROCESS_ERRORS
      OUTPUT_STRIP_TRAILING_WHITESPACE
      )
    string(REGEX MATCHALL "(^|\n)id: [0-9]+ or \"([^\n]+[0-9+])\"(\n|$)" ANDROID_SDK_TARGETS_FULL "${ANDROID_SDK_TARGETS_FULL}")

    SET(ANDROID_SDK_TARGETS "")
    if(ANDROID_PROCESS EQUAL 0)
      foreach(line ${ANDROID_SDK_TARGETS_FULL})
        string(REGEX REPLACE "(^|\n)id: [0-9]+ or \"([^\n]+[0-9+])\"(\n|$)" "\\2" line "${line}")
        list(APPEND ANDROID_SDK_TARGETS "${line}")
      endforeach()
    endif()
  endif()

  if(NOT ANDROID_PROCESS EQUAL 0)
    message(ERROR "Failed to get list of installed Android targets.")
    set(ANDROID_EXECUTABLE "ANDROID_EXECUTABLE-NOTFOUND")
A
Andrey Kamaev 已提交
121 122
  endif()

123
  # clear ANDROID_SDK_TARGET if no target is provided by user
A
Andrey Kamaev 已提交
124
  if(NOT ANDROID_SDK_TARGET)
125 126
    set(ANDROID_SDK_TARGET "" CACHE STRING "Android SDK target for the OpenCV Java API and samples")
  endif()
127
  if(ANDROID_SDK_TARGETS)
128 129
    set_property( CACHE ANDROID_SDK_TARGET PROPERTY STRINGS ${ANDROID_SDK_TARGETS} )
  endif()
A
Andrey Kamaev 已提交
130
endif(ANDROID_EXECUTABLE)
131 132 133 134 135 136 137

# finds minimal installed SDK target compatible with provided names or API levels
# usage:
#   get_compatible_android_api_level(VARIABLE [level1] [level2] ...)
macro(android_get_compatible_target VAR)
  set(${VAR} "${VAR}-NOTFOUND")
  if(ANDROID_SDK_TARGETS)
138
    list(GET ANDROID_SDK_TARGETS 0 __lvl)
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
    string(REGEX MATCH "[0-9]+$" __lvl "${__lvl}")

    #find minimal level mathing to all provided levels
    foreach(lvl ${ARGN})
      string(REGEX MATCH "[0-9]+$" __level "${lvl}")
      if(__level GREATER __lvl)
        set(__lvl ${__level})
      endif()
    endforeach()

    #search for compatible levels
    foreach(lvl ${ANDROID_SDK_TARGETS})
      string(REGEX MATCH "[0-9]+$" __level "${lvl}")
      if(__level EQUAL __lvl)
        #look for exact match
        foreach(usrlvl ${ARGN})
          if("${usrlvl}" STREQUAL "${lvl}")
            set(${VAR} "${lvl}")
            break()
          endif()
        endforeach()
        if("${${VAR}}" STREQUAL "${lvl}")
          break() #exact match was found
        elseif(NOT ${VAR})
          set(${VAR} "${lvl}")
        endif()
      elseif(__level GREATER __lvl)
        if(NOT ${VAR})
          set(${VAR} "${lvl}")
        endif()
        break()
      endif()
    endforeach()

    unset(__lvl)
    unset(__level)
  endif()
endmacro()
177

178
unset(__android_project_chain CACHE)
179 180

# add_android_project(target_name ${path} NATIVE_DEPS opencv_core LIBRARY_DEPS ${OpenCV_BINARY_DIR} SDK_TARGET 11)
181 182
macro(add_android_project target path)
  # parse arguments
M
Maksim Shabunin 已提交
183
  set(android_proj_arglist NATIVE_DEPS LIBRARY_DEPS SDK_TARGET IGNORE_JAVA IGNORE_MANIFEST COPY_LIBS)
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209
  set(__varname "android_proj_")
  foreach(v ${android_proj_arglist})
    set(${__varname}${v} "")
  endforeach()
  foreach(arg ${ARGN})
    set(__var "${__varname}")
    foreach(v ${android_proj_arglist})
      if("${v}" STREQUAL "${arg}")
        set(__varname "android_proj_${v}")
        break()
      endif()
    endforeach()
    if(__var STREQUAL __varname)
      list(APPEND ${__var} "${arg}")
    endif()
  endforeach()

  # get compatible SDK target
  android_get_compatible_target(android_proj_sdk_target ${ANDROID_NATIVE_API_LEVEL} ${android_proj_SDK_TARGET})

  if(NOT android_proj_sdk_target)
    message(WARNING "Can not find any SDK target compatible with: ${ANDROID_NATIVE_API_LEVEL} ${android_proj_SDK_TARGET}
                     The project ${target} will not be build")
  endif()

  # check native dependencies
A
Andrey Pavlenko 已提交
210 211 212 213 214
  if(android_proj_IGNORE_JAVA)
    ocv_check_dependencies(${android_proj_NATIVE_DEPS})
  else()
    ocv_check_dependencies(${android_proj_NATIVE_DEPS} opencv_java)
  endif()
215

216 217 218 219 220 221 222 223 224 225
  if(EXISTS "${path}/jni/Android.mk" )
    # find if native_app_glue is used
    file(STRINGS "${path}/jni/Android.mk" NATIVE_APP_GLUE REGEX ".*(call import-module,android/native_app_glue)" )
    if(NATIVE_APP_GLUE)
      if(ANDROID_NATIVE_API_LEVEL LESS 9 OR NOT EXISTS "${ANDROID_NDK}/sources/android/native_app_glue")
        set(OCV_DEPENDENCIES_FOUND FALSE)
      endif()
    endif()
  endif()

226 227 228 229
  if(OCV_DEPENDENCIES_FOUND AND android_proj_sdk_target AND ANDROID_EXECUTABLE AND ANT_EXECUTABLE AND ANDROID_TOOLS_Pkg_Revision GREATER 13 AND EXISTS "${path}/${ANDROID_MANIFEST_FILE}")

    project(${target})
    set(android_proj_bin_dir "${CMAKE_CURRENT_BINARY_DIR}/.build")
A
Andrey Kamaev 已提交
230

231 232 233
    # get project sources
    file(GLOB_RECURSE android_proj_files RELATIVE "${path}" "${path}/res/*" "${path}/src/*")

234 235 236 237
    if(NOT android_proj_IGNORE_MANIFEST)
      list(APPEND android_proj_files ${ANDROID_MANIFEST_FILE})
    endif()

238 239
    # copy sources out from the build tree
    set(android_proj_file_deps "")
240
    foreach(f ${android_proj_files})
241 242
      add_custom_command(
        OUTPUT "${android_proj_bin_dir}/${f}"
A
Alexander Alekhin 已提交
243
        COMMAND ${CMAKE_COMMAND} -E copy_if_different "${path}/${f}" "${android_proj_bin_dir}/${f}"
244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274
        MAIN_DEPENDENCY "${path}/${f}"
        COMMENT "Copying ${f}")
      list(APPEND android_proj_file_deps "${path}/${f}" "${android_proj_bin_dir}/${f}")
    endforeach()

    set(android_proj_lib_deps_commands "")
    set(android_proj_target_files ${ANDROID_PROJECT_FILES})
    ocv_list_add_prefix(android_proj_target_files "${android_proj_bin_dir}/")

    # process Android library dependencies
    foreach(dep ${android_proj_LIBRARY_DEPS})
      file(RELATIVE_PATH __dep "${android_proj_bin_dir}" "${dep}")
      list(APPEND android_proj_lib_deps_commands
        COMMAND ${ANDROID_EXECUTABLE} --silent update project --path "${android_proj_bin_dir}" --library "${__dep}")
    endforeach()

    # fix Android project
    add_custom_command(
        OUTPUT ${android_proj_target_files}
        COMMAND ${CMAKE_COMMAND} -E remove ${android_proj_target_files}
        COMMAND ${ANDROID_EXECUTABLE} --silent update project --path "${android_proj_bin_dir}" --target "${android_proj_sdk_target}" --name "${target}"
        ${android_proj_lib_deps_commands}
        MAIN_DEPENDENCY "${android_proj_bin_dir}/${ANDROID_MANIFEST_FILE}"
        DEPENDS "${path}/${ANDROID_MANIFEST_FILE}"
        COMMENT "Updating Android project at ${path}. SDK target: ${android_proj_sdk_target}"
        )

    list(APPEND android_proj_file_deps ${android_proj_target_files})

    # build native part
    file(GLOB_RECURSE android_proj_jni_files "${path}/jni/*.c" "${path}/jni/*.h" "${path}/jni/*.cpp" "${path}/jni/*.hpp")
275
    ocv_list_filterout(android_proj_jni_files "\\\\.svn")
276

A
Andrey Pavlenko 已提交
277
    if(android_proj_jni_files AND EXISTS ${path}/jni/Android.mk AND NOT DEFINED JNI_LIB_NAME)
278
      # find local module name in Android.mk file to build native lib
279 280 281 282
      file(STRINGS "${path}/jni/Android.mk" JNI_LIB_NAME REGEX "LOCAL_MODULE[ ]*:=[ ]*.*" )
      string(REGEX REPLACE "LOCAL_MODULE[ ]*:=[ ]*([a-zA-Z_][a-zA-Z_0-9]*)[ ]*" "\\1" JNI_LIB_NAME "${JNI_LIB_NAME}")

      if(JNI_LIB_NAME)
283
        if(NATIVE_APP_GLUE)
284 285
          include_directories(${ANDROID_NDK}/sources/android/native_app_glue)
          list(APPEND android_proj_jni_files ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c)
286
          ocv_warnings_disable(CMAKE_C_FLAGS -Wstrict-prototypes -Wunused-parameter -Wmissing-prototypes)
287 288 289
          set(android_proj_NATIVE_DEPS ${android_proj_NATIVE_DEPS} android)
        endif()

290
        add_library(${JNI_LIB_NAME} MODULE ${android_proj_jni_files})
A
Alexander Alekhin 已提交
291 292 293
        ocv_target_include_modules_recurse(${JNI_LIB_NAME} ${android_proj_NATIVE_DEPS})
        ocv_target_include_directories(${JNI_LIB_NAME} "${path}/jni")
        ocv_target_link_libraries(${JNI_LIB_NAME} ${OPENCV_LINKER_LIBS} ${android_proj_NATIVE_DEPS})
294 295 296 297 298 299 300

        set_target_properties(${JNI_LIB_NAME} PROPERTIES
            OUTPUT_NAME "${JNI_LIB_NAME}"
            LIBRARY_OUTPUT_DIRECTORY "${android_proj_bin_dir}/libs/${ANDROID_NDK_ABI_NAME}"
            )

        get_target_property(android_proj_jni_location "${JNI_LIB_NAME}" LOCATION)
301 302 303
        if (NOT (CMAKE_BUILD_TYPE MATCHES "debug"))
            add_custom_command(TARGET ${JNI_LIB_NAME} POST_BUILD COMMAND ${CMAKE_STRIP} --strip-unneeded "${android_proj_jni_location}")
        endif()
304 305 306 307
      endif()
    endif()

    # build java part
A
Andrey Pavlenko 已提交
308 309 310 311 312 313 314
    if(android_proj_IGNORE_JAVA)
      add_custom_command(
         OUTPUT "${android_proj_bin_dir}/bin/${target}-debug.apk"
         COMMAND ${ANT_EXECUTABLE} -q -noinput -k debug
         COMMAND ${CMAKE_COMMAND} -E touch "${android_proj_bin_dir}/bin/${target}-debug.apk" # needed because ant does not update the timestamp of updated apk
         WORKING_DIRECTORY "${android_proj_bin_dir}"
         MAIN_DEPENDENCY "${android_proj_bin_dir}/${ANDROID_MANIFEST_FILE}"
315
         DEPENDS ${android_proj_file_deps} ${JNI_LIB_NAME})
A
Andrey Pavlenko 已提交
316
    else()
317 318 319 320 321 322
      add_custom_command(
         OUTPUT "${android_proj_bin_dir}/bin/${target}-debug.apk"
         COMMAND ${ANT_EXECUTABLE} -q -noinput -k debug
         COMMAND ${CMAKE_COMMAND} -E touch "${android_proj_bin_dir}/bin/${target}-debug.apk" # needed because ant does not update the timestamp of updated apk
         WORKING_DIRECTORY "${android_proj_bin_dir}"
         MAIN_DEPENDENCY "${android_proj_bin_dir}/${ANDROID_MANIFEST_FILE}"
323
         DEPENDS "${OpenCV_BINARY_DIR}/bin/classes.jar.dephelper" opencv_java # as we are part of OpenCV we can just force this dependency
324
         DEPENDS ${android_proj_file_deps} ${JNI_LIB_NAME})
A
Andrey Pavlenko 已提交
325
    endif()
326

A
Andrey Kamaev 已提交
327 328
    unset(JNI_LIB_NAME)

329
    add_custom_target(${target} ALL SOURCES "${android_proj_bin_dir}/bin/${target}-debug.apk" )
A
Andrey Pavlenko 已提交
330 331 332 333 334 335
    if(NOT android_proj_IGNORE_JAVA)
      add_dependencies(${target} opencv_java)
    endif()
    if(android_proj_native_deps)
      add_dependencies(${target} ${android_proj_native_deps})
    endif()
336

M
Maksim Shabunin 已提交
337 338
    if (android_proj_COPY_LIBS OR ANDROID_EXAMPLES_WITH_LIBS)
      message(STATUS "Android project with libs: " ${target})
339 340 341 342 343 344
      add_custom_target(
        ${target}_copy_libs
        COMMAND ${CMAKE_COMMAND} -DSRC_DIR=${OpenCV_BINARY_DIR}/lib -DDST_DIR=${android_proj_bin_dir}/libs -P ${OpenCV_SOURCE_DIR}/cmake/copyAndroidLibs.cmake
        WORKING_DIRECTORY ${OpenCV_BINARY_DIR}/lib
      )
      add_dependencies(${target} ${target}_copy_libs)
M
Maksim Shabunin 已提交
345 346 347
      if (ANDROID_EXAMPLES_WITH_LIBS)
        add_dependencies(${target}_copy_libs "${OpenCV_BINARY_DIR}/bin/classes.jar.dephelper" opencv_java)
      endif()
348 349
    endif()

350 351 352 353 354
    if(__android_project_chain)
      add_dependencies(${target} ${__android_project_chain})
    endif()
    set(__android_project_chain ${target} CACHE INTERNAL "auxiliary variable used for Android progects chaining")

355
    # put the final .apk to the OpenCV's bin folder
A
Alexander Alekhin 已提交
356
    add_custom_command(TARGET ${target} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different "${android_proj_bin_dir}/bin/${target}-debug.apk" "${OpenCV_BINARY_DIR}/bin/${target}.apk")
V
Vadim Pisarevsky 已提交
357
    if(INSTALL_ANDROID_EXAMPLES AND "${target}" MATCHES "^example-")
358
      #apk
359
      install(FILES "${OpenCV_BINARY_DIR}/bin/${target}.apk" DESTINATION "samples" COMPONENT samples)
360 361
      get_filename_component(sample_dir "${path}" NAME)
      #java part
362
      list(REMOVE_ITEM android_proj_files ${ANDROID_MANIFEST_FILE})
363 364
      foreach(f ${android_proj_files} ${ANDROID_MANIFEST_FILE})
        get_filename_component(install_subdir "${f}" PATH)
365
        install(FILES "${android_proj_bin_dir}/${f}" DESTINATION "samples/${sample_dir}/${install_subdir}" COMPONENT samples)
366 367
      endforeach()
      #jni part + eclipse files
368
      file(GLOB_RECURSE jni_files RELATIVE "${path}" "${path}/jni/*" "${path}/.cproject")
369 370 371
      ocv_list_filterout(jni_files "\\\\.svn")
      foreach(f ${jni_files} ".classpath" ".project" ".settings/org.eclipse.jdt.core.prefs")
        get_filename_component(install_subdir "${f}" PATH)
372
        install(FILES "${path}/${f}" DESTINATION "samples/${sample_dir}/${install_subdir}" COMPONENT samples)
373 374 375 376 377 378 379
      endforeach()
      #update proj
      if(android_proj_lib_deps_commands)
        set(inst_lib_opt " --library ../../sdk/java")
      endif()
      install(CODE "EXECUTE_PROCESS(COMMAND ${ANDROID_EXECUTABLE} --silent update project --path . --target \"${android_proj_sdk_target}\" --name \"${target}\" ${inst_lib_opt}
                                    WORKING_DIRECTORY \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/samples/${sample_dir}\"
380
                                   )"  COMPONENT samples)
381
      #empty 'gen'
382
      install(CODE "MAKE_DIRECTORY(\"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/samples/${sample_dir}/gen\")" COMPONENT samples)
383 384 385
    endif()
  endif()
endmacro()