# # Functions for detection and usage of then Android SDK # Used when cross compiling for Android # # The Android device emulator (included in the SDK) can # be "remote" controlled by the functions and macros # implemented in this file. # They are employing a pseudo object-oriented approach # to handle an emulator instance, that is an emulator # instance is described by a so called emulator instance # object handle which has to be passed to all functions # operating on it as their first argument. # Android emulator object handles consist of the # following properties, which need to be tracked to # describe and Android emulator instance: # EMULATOR_STATE: the state of the emulator instance, # can be one of the following values: # * STARTING - the instance has been initialized # and the emulator has been executed # * RUNNING - the emulated device is online and # can receive instructions # * STOPPED - the emulated device has been # shutdown or is currently being # shutdown # EMULATOR_UUID: a generated ID to identify the emulator # instance. Meant to prevent accessing the wrong device # in case multiple Android devices are accessible. # EMULATOR_NAME: the name of the emulator instance. # all running emulator instances are named by the SDK # in a locally unique fashion. The name will only be # available in case the state is 'RUNNING', since # only the name of running devices can be retrieved # by UUID matching. # An emulator instance is accessed by the different # tools of the NDK (e. g. 'adb') by referring to the # instance with the value of EMULATOR_NAME. include(CMakeParseArguments) # # 'Unpacks' an Android emulator instance handle object to # individual components. # VAR - the emulator instance handle object to unpack # EMULATOR_STATE - will contain the emulators state # EMULATOR_UUID - will contain the generated UUID that # identifies the emulator instance # EMULATOR_NAME - will contain the instance name, required # to access the emulator via 'adb' etc. # All three outputs will be unset if the object in VAR # is not a valid emulator instance handle. # All additional arguments will be ignored. # macro(DCMTK_ANDROID_GET_OBJECT_PROPERTIES VAR) list(LENGTH ${VAR} ${VAR}_LENGTH) if(${VAR}_LENGTH EQUAL 3) list(GET ${VAR} 0 EMULATOR_STATE) list(GET ${VAR} 1 EMULATOR_UUID) list(GET ${VAR} 2 EMULATOR_NAME) else() unset(EMULATOR_STATE) unset(EMULATOR_UUID) unset(EMULATOR_NAME) endif() endmacro() # # Stores the properties of an emulator instance inside an # emulator instance handle object. # VAR - the name of the variable that shall contain the # resulting instance handle object. # EMULATOR_STATE - the state to set # EMULATOR_UUID - the UUID to set # EMULATOR_NAME - the name to set # All additional arguments will be ignored. # macro(DCMTK_ANDROID_SET_OBJECT_PROPERTIES VAR EMULATOR_STATE EMULATOR_UUID EMULATOR_NAME) set(${VAR} "${EMULATOR_STATE}" "${EMULATOR_UUID}" "${EMULATOR_NAME}" CACHE INTERNAL "") endmacro() # # Destroys an instance handle object. # VAR - the name of the variable its emulator instance handle # shall be object destroyed # All additional arguments will be ignored. # macro(DCMTK_ANDROID_DESTROY_OBJECT VAR) unset("${VAR}" CACHE) endmacro() # # Tries to detect any required shared objects within the NDK # VAR - the name of the variable that will receive a list # of required shared object files (if any) # Will set VAR to an empty list if no shared objects are # required (e. g. when they are linked statically). # All additional arguments will be ignored. # function(DCMTK_ANDROID_FIND_RUNTIME_LIBRARIES VAR) set(CMAKE_FIND_LIBRARY_SUFFIXES ".so") foreach(DIR ${ANDROID_STL_INCLUDE_DIRS}) if(CMAKE_VERSION VERSION_LESS 2.8.11) get_filename_component(DIR "${DIR}" PATH) else() get_filename_component(DIR "${DIR}" DIRECTORY) endif() list(APPEND ANDROID_STL_LIBRARY_DIRS "${DIR}") endforeach() find_library(ANDROID_STL_SHARED_OBJECT ${ANDROID_STL} PATHS ${ANDROID_STL_LIBRARY_DIRS} NO_DEFAULT_PATH NO_CMAKE_FIND_ROOT_PATH) if(ANDROID_STL_SHARED_OBJECT) set("${VAR}" ${ANDROID_STL_SHARED_OBJECT} PARENT_SCOPE) endif() endfunction() # # Tries to detect the required executables and scripts automatically, # which should be contained in the Android SDK. Emits an error that # will halt the configuration process if that is not possible. # Will also detect all available AVDs after tool detection was # successful. # All arguments will be ignored. # function(DCMTK_SETUP_ANDROID_EMULATOR) if(NOT ANDROID_TEMPORARY_FILES_LOCATION) set(ANDROID_TEMPORARY_FILES_LOCATION "/cache" CACHE STRING "The path on the Android device that should be used for temporary files") endif() if(NOT ANDROID_SDK_ROOT) if(CMAKE_HOST_SYSTEM MATCHES "Windows.*") file(TO_CMAKE_PATH "$ENV{PROGRAMFILES}" ANDROID_SDK_SEARCH_PATHS) set(ANDROID_SDK_ROOT "${ANDROID_SDK_SEARCH_PATHS}/android-sdk" CACHE PATH "Location of the Android SDK") else() set(ANDROID_SDK_ROOT "/opt/android-sdk" CACHE PATH "Location of the Android SDK") endif() endif() find_program(ANDROID_EMULATOR_PROGRAM emulator PATHS ${ANDROID_SDK_ROOT} PATH_SUFFIXES tools NO_DEFAULT_PATH NO_CMAKE_FIND_ROOT_PATH) if(CMAKE_HOST_SYSTEM MATCHES "Windows.*") find_program(ANDROID_ANDROID_PROGRAM android.bat PATHS ${ANDROID_SDK_ROOT} PATH_SUFFIXES tools NO_DEFAULT_PATH NO_CMAKE_FIND_ROOT_PATH) else() find_program(ANDROID_ANDROID_PROGRAM android PATHS ${ANDROID_SDK_ROOT} PATH_SUFFIXES tools NO_DEFAULT_PATH NO_CMAKE_FIND_ROOT_PATH) endif() find_program(ANDROID_ADB_PROGRAM adb PATHS ${ANDROID_SDK_ROOT} PATH_SUFFIXES platform-tools NO_DEFAULT_PATH NO_CMAKE_FIND_ROOT_PATH) if(NOT ANDROID_EMULATOR_PROGRAM OR NOT ANDROID_ANDROID_PROGRAM OR NOT ANDROID_ADB_PROGRAM) message(FATAL_ERROR "Failed to detect the Android SDK, please set ANDROID_SDK_ROOT to the location of your Android SDK" "or set the missing tools manually!" ) else() execute_process(COMMAND "${ANDROID_ANDROID_PROGRAM}" list avd RESULT_VARIABLE RESULT OUTPUT_VARIABLE OUTPUT ERROR_QUIET) string(REGEX MATCHALL "Name:[ \t]*[^\r\n]*" ANDROID_AVAILABLE_AVDS ${OUTPUT}) string(REGEX REPLACE "Name:[ \t]*([^\r\n;]*)" "\\1" ANDROID_AVAILABLE_AVDS "${ANDROID_AVAILABLE_AVDS}") set(ANDROID_EMULATOR_AVD "${ANDROID_EMULATOR_AVD}" CACHE STRING "Android emulator Android Virtual Device (AVD) configuration" FORCE) set_property(CACHE ANDROID_EMULATOR_AVD PROPERTY STRINGS ${ANDROID_AVAILABLE_AVDS}) endif() endfunction() # # Enumerates all currently available Android devices # (started emulators and connected devices) and separates # them into two different lists. # ONLINE - the name of the variable that will be set to a # list of all currently online devices' names # (devices that can receive instructions) # OFFLINE - the name of the variable that will be set to a # list of all currently offline device's names # (devices that are connected but cannot receive # instructions) # Will ignore all additional arguments. # function(DCMTK_ANDROID_LIST_EMULATORS ONLINE OFFLINE) DCMTK_SETUP_ANDROID_EMULATOR() if(ANDROID_ADB_PROGRAM) execute_process( COMMAND "${ANDROID_ADB_PROGRAM}" devices RESULT_VARIABLE RESULT OUTPUT_VARIABLE DEVICES_RAW ERROR_QUIET ) string(REPLACE "\n" ";" DEVICES "${DEVICES_RAW}") foreach(DEVICE ${DEVICES}) string(REGEX REPLACE "(.+)\t(.+)" "\\1;\\2" DS "${DEVICE}") list(LENGTH DS DSL) if(DSL EQUAL 2) list(GET DS 0 EMULATOR_NAME) list(GET DS 1 EMULATOR_STATE) if(EMULATOR_STATE MATCHES "^device$") list(APPEND "${ONLINE}" ${EMULATOR_NAME}) else() list(APPEND "${OFFLINE}" ${EMULATOR_NAME}) endif() endif() endforeach() endif() set("${ONLINE}" ${${ONLINE}} PARENT_SCOPE) set("${OFFLINE}" ${${OFFLINE}} PARENT_SCOPE) endfunction() # # Generate a random ID that is hopefully unique # enough to be used as the instance UUID. # VAR - the name of the variable that will receive # the generated UUID as a string value # Will ignore all additional arguments. # if(CMAKE_VERSION VERSION_LESS 2.8.11) macro(DCMTK_ANDROID_EMULATOR_GENERATE_UUID VAR) string(RANDOM LENGTH 20 ALPHABET "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" ${VAR}) endmacro() else() function(DCMTK_ANDROID_EMULATOR_GENERATE_UUID VAR) string(RANDOM LENGTH 20 RAND) string(TIMESTAMP TM) set(${VAR} "${TM}${RAND}") string(MD5 ${VAR} ${${VAR}}) set(${VAR} ${${VAR}} PARENT_SCOPE) endfunction() endif() # # Tries to query the UUID property of an accessible Android device. # EMULATOR_NAME - the device name, as returned by DCMTK_ANDROID_LIST_EMULATORS # VAR - the name of the variable that will be set to the device UUID # Will unset the variable referred to by VAR if no device with the given name # is accessible or the device is offline. # Will ignore all additional arguments. # function(DCMTK_ANDROID_GET_EMULATOR_UUID EMULATOR_NAME VAR) execute_process( COMMAND "${ANDROID_ADB_PROGRAM}" -s "${EMULATOR_NAME}" shell getprop "ro.emu.uuid" RESULT_VARIABLE RESULT OUTPUT_VARIABLE OUTPUT ERROR_QUIET ) DCMTK_UNSET_PARENT_SCOPE(${VAR}) if(NOT RESULT) string(STRIP "${OUTPUT}" UUID) if(UUID) set("${VAR}" ${UUID} PARENT_SCOPE) endif() endif() endfunction() # # Retrieves the name of the emulator instance referred to by # the given UUID. Will wait until the device becomes online # to query the name. # VAR - the name of the variable that will be set # to the device name # EMULATOR_UUID - the emulator UUID of the device to inquired # Will wait until all available devices are online or the right # one has been found. # Will ignore all additional arguments. # function(DCMTK_ANDROID_GET_EMULATOR_NAME VAR EMULATOR_UUID) DCMTK_ANDROID_LIST_EMULATORS(ONLINE_EMULATORS OFFLINE_EMULATORS) foreach(EMULATOR ${ONLINE_EMULATORS}) DCMTK_ANDROID_GET_EMULATOR_UUID("${EMULATOR}" UUID) if(EMULATOR_UUID STREQUAL UUID) set("${VAR}" "${EMULATOR}" PARENT_SCOPE) return() endif() endforeach() while(OFFLINE_EMULATORS) list(GET OFFLINE_EMULATORS 0 EMULATOR) list(REMOVE_AT OFFLINE_EMULATORS 0) execute_process( COMMAND "${ANDROID_ADB_PROGRAM}" -s "${EMULATOR}" wait-for-device TIMEOUT 1 RESULT_VARIABLE RESULT OUTPUT_QUIET ERROR_QUIET ) if(NOT RESULT) DCMTK_ANDROID_GET_EMULATOR_UUID("${EMULATOR}" UUID) if(UUID) if(EMULATOR_UUID STREQUAL UUID) set("${VAR}" "${EMULATOR}" PARENT_SCOPE) return() endif() else() list(APPEND OFFLINE_EMULATORS "${EMULATOR}") endif() else() list(APPEND OFFLINE_EMULATORS "${EMULATOR}") endif() endwhile() endfunction() # # Sets up and starts a new emulator instance or reuses an # existing one. # VAR - the emulator instance handle object to reuse or # initialize # Will test if VAR refers to a valid emulator instance # handle and, if so, ensure the device is started. # Will initialize a new emulator instance and start it, # if VAR does not refer to a valid emulator instance. # Will ignore all additional arguments. # function(DCMTK_ANDROID_START_EMULATOR VAR) DCMTK_SETUP_ANDROID_EMULATOR() if(NOT ANDROID_EMULATOR_AVD) message(FATAL_ERROR "Please select which Android emulator Android Virtual Device (AVD) configuration to use!") else() DCMTK_ANDROID_GET_OBJECT_PROPERTIES("${VAR}") if(NOT EMULATOR_STATE) DCMTK_ANDROID_EMULATOR_GENERATE_UUID(EMULATOR_UUID) elseif(EMULATOR_STATE STREQUAL "RUNNING") DCMTK_ANDROID_GET_EMULATOR_UUID("${EMULATOR_NAME}" UUID) # Do nothing if the running emulator instance is ok and can be reused. # Otherwise restart it. if(UUID STREQUAL EMULATOR_UUID) message(STATUS "Reusing already running Android device emulator...") return() endif() elseif(EMULATOR_STATE STREQUAL "STARTING") # Is it really starting, or has somebody aborted it? message(STATUS "Found previously started Android device emulator, checking if it's still present...") DCMTK_ANDROID_GET_EMULATOR_NAME(EMULATOR_NAME "${EMULATOR_UUID}") if(EMULATOR_NAME) message(STATUS "Found previously started Android device emulator, checking if it's still present... yes") DCMTK_ANDROID_SET_OBJECT_PROPERTIES(${VAR} RUNNING "${EMULATOR_UUID}" "${EMULATOR_NAME}") return() endif() message(STATUS "Found previously started Android device emulator, checking if it's still present... no") endif() message(STATUS "Starting the Android device emulator...") if(CMAKE_HOST_SYSTEM MATCHES "Windows.*") set(COMMAND sh -c "${ANDROID_EMULATOR_PROGRAM} -avd ${ANDROID_EMULATOR_AVD} -no-boot-anim -prop ro.emu.uuid=${EMULATOR_UUID} >${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/android-emulator.log 2>&1 < /dev/null &") else() set(COMMAND sh -c "${ANDROID_EMULATOR_PROGRAM} -avd ${ANDROID_EMULATOR_AVD} -no-window -no-boot-anim -prop ro.emu.uuid=${EMULATOR_UUID} >${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/android-emulator.log 2>&1 < /dev/null &") endif() execute_process( COMMAND ${COMMAND} RESULT_VARIABLE RESULT OUTPUT_QUIET ERROR_QUIET ) if(NOT RESULT) DCMTK_ANDROID_SET_OBJECT_PROPERTIES("${VAR}" STARTING "${EMULATOR_UUID}" "") else() DCMTK_ANDROID_DESTROY_OBJECT("${VAR}") message(FATAL_ERROR "Error starting Android emulator.") endif() endif() endfunction() # # Restart adb/the emulated device in root mode so that we gain write access to # the device. # Newer versions of the SDK seem to require this for doing anything meaningful # with it. # EMULATOR_NAME - the name of the emulated device that shall be rooted. # Will ignore all additional arguments. # function(DCMTK_ANDROID_ADB_ROOT EMULATOR_NAME) execute_process( COMMAND "${ANDROID_ADB_PROGRAM}" -s "${EMULATOR_NAME}" root OUTPUT_QUIET ERROR_QUIET ) # the SDK was seemingly designed by a five year old, the device will # become invisible while it is being rooted, therefore, wait until # it is ready again set(STATUS 1) while(STATUS) execute_process( COMMAND "${ANDROID_ADB_PROGRAM}" -s "${EMULATOR_NAME}" wait-for-device RESULT_VARIABLE STATUS OUTPUT_QUIET ERROR_QUIET ) endwhile() endfunction() # # Waits until the given emulator instance becomes online (RUNNING). # VAR - the emulator instance handle object referring to the emulator # to wait for # Will fail with a warning message if the given variable does not # refer to a valid emulator instance handle object. # Will set the object's state to "RUNNING" when successful. # Will wait indefinitely if no such device exists or the device does # not become online. # Will ignore all additional arguments. # function(DCMTK_ANDROID_WAIT_FOR_EMULATOR VAR) DCMTK_ANDROID_GET_OBJECT_PROPERTIES("${VAR}") if(NOT EMULATOR_STATE) message(AUTHOR_WARNING "Error: ${VAR} is not a valid Android emulator instance handle.") elseif(EMULATOR_STATE STREQUAL "RUNNING") # Emulator is running, no need to wait! elseif(EMULATOR_STATE STREQUAL "STOPPED") message(WARNING "The Android emulator has stopped, aborting...") else() message(STATUS "Waiting until the Android device emulator is ready to receive instructions...") while(NOT EMULATOR_NAME) DCMTK_ANDROID_GET_EMULATOR_NAME(EMULATOR_NAME "${EMULATOR_UUID}") endwhile() DCMTK_ANDROID_ADB_ROOT("${EMULATOR_NAME}") DCMTK_ANDROID_SET_OBJECT_PROPERTIES("${VAR}" RUNNING "${EMULATOR_UUID}" "${EMULATOR_NAME}") endif() endfunction() # # Prepare a command that will shutdown the given Android emulator when # in the local shell run. # VAR - the name of the variable that will be set to the # generated command # EMULATOR_NAME - the name of the emulated device that shall be shut # down # Will unset the variable referred to by VAR if no such command can # be generated for the current host platform (Windows). # Will ignore all additional arguments. # macro(DCMTK_ANDROID_STOP_EMULATOR_COMMAND VAR EMULATOR_NAME) if(CMAKE_HOST_SYSTEM MATCHES "Windows.*") unset("${VAR}") else() set("${VAR}" "${ANDROID_ADB_PROGRAM}" -s "${EMULATOR_NAME}" emu kill) endif() endmacro() # # Prepare an emulator shutdown message or a warning message if automated # shutdown is not possible. # VAR - the name of the variable that will be set to the generated message # Will ignore all additional arguments. # macro(DCMTK_ANDROID_EMULATOR_SHUTDOWN_MESSAGE VAR) if(CMAKE_HOST_SYSTEM MATCHES "Windows.*") set("${VAR}" WARNING "The Android device emulator can't be terminated automatically under Windows, please shutdown \"${EMULATOR_NAME}\" manually!") else() set("${VAR}" STATUS "Shutting down the Android device emulator...") endif() endmacro() # # Stops the emulator and sets its state to STOPPED when successful. # VAR - the name of the variable referring to the emulator instance # handle object # Will emit a warning message if the emulator can't be stopped and # won't modify its state in that case. # Will ignore all additional arguments. # function(DCMTK_ANDROID_STOP_EMULATOR VAR) DCMTK_ANDROID_GET_OBJECT_PROPERTIES("${VAR}") if(NOT EMULATOR_STATE) message(AUTHOR_WARNING "Error: ${VAR} is not a valid Android emulator instance handle.") elseif(EMULATOR_STATE STREQUAL "STARTING") # Can't tell it to stop if it is not done starting DCMTK_ANDROID_WAIT_FOR_EMULATOR("${VAR}") elseif(EMULATOR_STATE STREQUAL "STOPPED") # Emulator is already stopped, do nothing return() endif() DCMTK_ANDROID_EMULATOR_SHUTDOWN_MESSAGE(MESSAGE) message(${MESSAGE}) if(NOT CMAKE_HOST_SYSTEM MATCHES "Windows.*") DCMTK_ANDROID_STOP_EMULATOR_COMMAND(COMMAND "${EMULATOR_NAME}") execute_process( COMMAND ${COMMAND} RESULT_VARIABLE RESULT OUTPUT_QUIET ERROR_QUIET ) if(NOT RESULT) DCMTK_ANDROID_SET_OBJECT_PROPERTIES("${VAR}" STOPPED "${EMULATOR_UUID}" "") else() message(WARNING "Unable to stop the android device emulator, please shutdown \"${EMULATOR_NAME}\" manually!") endif() endif() endfunction() # # Uploads local files to the given Android device using 'adb push'. # VAR - the emulator instance handle object referring to the target # device # Requires the device to be running. # Additional arguments will be parsed and interpreted similar to # CMake's file(COPY ...) command. # function(DCMTK_ANDROID_PUSH VAR) DCMTK_ANDROID_GET_OBJECT_PROPERTIES("${VAR}") if(NOT EMULATOR_STATE) message(AUTHOR_WARNING "Error: ${VAR} is not a valid Android emulator instance handle.") elseif(EMULATOR_STATE STREQUAL "RUNNING") cmake_parse_arguments(DCMTK_ANDROID_PUSH "" "" "DESTINATION" ${ARGN}) foreach(LOCAL_FILE ${DCMTK_ANDROID_PUSH_UNPARSED_ARGUMENTS}) execute_process( COMMAND "${ANDROID_ADB_PROGRAM}" -s "${EMULATOR_NAME}" push "${LOCAL_FILE}" "${DCMTK_ANDROID_PUSH_DESTINATION}" RESULT_VARIABLE RESULT OUTPUT_QUIET ERROR_QUIET ) endforeach() else() message(AUTHOR_WARNING "Error: the Android emulator \"${VAR}\" is not ready to receive commands") endif() endfunction() # # Downloads remote files from the given Android device using 'adb pull'. # VAR - the emulator instance handle object referring to the target # device # Requires the device to be running. # Additional arguments will be parsed and interpreted similar to # CMake's file(COPY ...) command. # function(DCMTK_ANDROID_PULL VAR) DCMTK_ANDROID_GET_OBJECT_PROPERTIES("${VAR}") if(NOT EMULATOR_STATE) message(AUTHOR_WARNING "Error: ${VAR} is not a valid Android emulator instance handle.") elseif(EMULATOR_STATE STREQUAL "RUNNING") cmake_parse_arguments(DCMTK_ANDROID_PULL "" "" "DESTINATION" ${ARGN}) foreach(REMOTE_FILE ${DCMTK_ANDROID_PULL_UNPARSED_ARGUMENTS}) execute_process( COMMAND "${ANDROID_ADB_PROGRAM}" -s "${EMULATOR_NAME}" pull "${REMOTE_FILE}" "${DCMTK_ANDROID_PULL_DESTINATION}" RESULT_VARIABLE RESULT OUTPUT_QUIET ERROR_QUIET ) endforeach() else() message(AUTHOR_WARNING "Error: the Android emulator \"${VAR}\" is not ready to receive commands") endif() endfunction() # # Executes a shell command on the given Android device using 'adb shell'. # VAR - the emulator instance handle object referring to the target # device # Requires the device to be running. # Additional arguments will be parsed and interpreted similar to # CMake's execute_process(...) command. # function(DCMTK_ANDROID_SHELL VAR) DCMTK_ANDROID_GET_OBJECT_PROPERTIES("${VAR}") if(NOT EMULATOR_STATE) message(AUTHOR_WARNING "Error: ${VAR} is not a valid Android emulator instance handle.") elseif(EMULATOR_STATE STREQUAL "RUNNING") cmake_parse_arguments(DCMTK_ANDROID_SHELL "OUTPUT_QUIET;ERROR_QUIET" "RESULT_VARIABLE;OUTPUT_VARIABLE;ERROR_VARIABLE;WORKING_DIRECTORY" "COMMAND" ${ARGN} ) # Prepare commandline set(COMMAND "${ANDROID_ADB_PROGRAM}" -s "${EMULATOR_NAME}" shell) if(DCMTK_ANDROID_SHELL_WORKING_DIRECTORY) list(APPEND COMMAND "cd" "${DCMTK_ANDROID_SHELL_WORKING_DIRECTORY}" "&&") endif() list(APPEND COMMAND "${DCMTK_ANDROID_SHELL_COMMAND}") # Inspect output variable parameters if(DCMTK_ANDROID_SHELL_OUTPUT_VARIABLE) set(PUSH_OUTPUT_VAR TRUE) if(DCMTK_ANDROID_SHELL_ERROR_VARIABLE) if(DCMTK_ANDROID_SHELL_ERROR_VARIABLE STREQUAL DCMTK_ANDROID_SHELL_OUTPUT_VARIABLE) set(STREAMS_MERGED TRUE) else() set(PUSH_ERROR_VAR TRUE) endif() else() set(DCMTK_ANDROID_SHELL_ERROR_VARIABLE DCMTK_ANDROID_SHELL_ERROR_MESSAGE_BUFFER) endif() else() set(DCMTK_ANDROID_SHELL_OUTPUT_VARIABLE DCMTK_ANDROID_SHELL_OUTPUT_MESSAGE_BUFFER) if(DCMTK_ANDROID_SHELL_ERROR_VARIABLE) set(PUSH_ERROR_VAR TRUE) else() set(STREAMS_MERGED TRUE) endif() endif() # Prefix to prevent collision of output capturing files if(CMAKE_VERSION VERSION_LESS 2.8.7) string(RANDOM LENGTH 20 ALPHABET "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" PREFIX) else() string(MD5 PREFIX "${DCMTK_ANDROID_SHELL_COMMAND}") endif() # Prepare output redirection (buffering) if(STREAMS_MERGED) list(APPEND COMMAND > "${ANDROID_TEMPORARY_FILES_LOCATION}/${PREFIX}_output" 2>&1) else() list(APPEND COMMAND > "${ANDROID_TEMPORARY_FILES_LOCATION}/${PREFIX}_output" 2> "${ANDROID_TEMPORARY_FILES_LOCATION}/${PREFIX}_error") endif() # Prepare capturing the result list(APPEND COMMAND "\;" echo -n $? > "${ANDROID_TEMPORARY_FILES_LOCATION}/${PREFIX}_result") # Run the command execute_process( COMMAND ${COMMAND} RESULT_VARIABLE ADB_RESULT OUTPUT_VARIABLE ADB_OUTPUT ERROR_VARIABLE ADB_OUTPUT ) # Deal with it if(ADB_RESULT) message(WARNING "Error sending command to the Android emulator, adb reported the error: ${ADB_OUTPUT}") if(DCMTK_ANDROID_SHELL_RESULT_VARIABLE) set("${DCMTK_ANDROID_SHELL_RESULT_VARIABLE}" ${ADB_RESULT} PARENT_SCOPE) endif() else() # Receive results DCMTK_ANDROID_PULL("${VAR}" "${ANDROID_TEMPORARY_FILES_LOCATION}/${PREFIX}_output" "${ANDROID_TEMPORARY_FILES_LOCATION}/${PREFIX}_error" "${ANDROID_TEMPORARY_FILES_LOCATION}/${PREFIX}_result" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}" ) # Cleanup temp files execute_process( COMMAND "${ANDROID_ADB_PROGRAM}" -s "${EMULATOR_NAME}" shell rm "${ANDROID_TEMPORARY_FILES_LOCATION}/${PREFIX}_output" "\;" rm "${ANDROID_TEMPORARY_FILES_LOCATION}/${PREFIX}_error" "\;" rm "${ANDROID_TEMPORARY_FILES_LOCATION}/${PREFIX}_result" RESULT_VARIABLE RESULT_QUIET OUTPUT_QUIET ERROR_QUIET ) # Analyze results if(DCMTK_ANDROID_SHELL_RESULT_VARIABLE) file(READ "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/${PREFIX}_result" "${DCMTK_ANDROID_SHELL_RESULT_VARIABLE}") set("${DCMTK_ANDROID_SHELL_RESULT_VARIABLE}" ${${DCMTK_ANDROID_SHELL_RESULT_VARIABLE}} PARENT_SCOPE) endif() if(NOT DCMTK_ANDROID_SHELL_OUTPUT_QUIET OR (STREAMS_MERGED AND NOT DCMTK_ANDROID_SHELL_ERROR_QUIET)) file(READ "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/${PREFIX}_output" "${DCMTK_ANDROID_SHELL_OUTPUT_VARIABLE}") if(PUSH_OUTPUT_VAR) set("${DCMTK_ANDROID_SHELL_OUTPUT_VARIABLE}" ${${DCMTK_ANDROID_SHELL_OUTPUT_VARIABLE}} PARENT_SCOPE) else() message("${${DCMTK_ANDROID_SHELL_OUTPUT_VARIABLE}}") endif() endif() if(NOT DCMTK_ANDROID_SHELL_ERROR_QUIET AND NOT STREAMS_MERGED) file(READ "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/${PREFIX}_error" "${DCMTK_ANDROID_SHELL_ERROR_VARIABLE}") if(PUSH_ERROR_VAR) set("${DCMTK_ANDROID_SHELL_ERROR_VARIABLE}" ${${DCMTK_ANDROID_SHELL_ERROR_VARIABLE}} PARENT_SCOPE) else() message("${${DCMTK_ANDROID_SHELL_ERROR_VARIABLE}}") endif() endif() # Cleanup temp files file(REMOVE "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/${PREFIX}_result") file(REMOVE "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/${PREFIX}_output") file(REMOVE "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/${PREFIX}_error") endif() else() message(AUTHOR_WARNING "Error: the Android emulator \"${VAR}\" is not ready to receive commands") endif() endfunction()