1# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2# file Copyright.txt or https://cmake.org/licensing for details.
3
4# This function handles pushing all of the test files needed to the device.
5# It places the data files in the object store and makes links to them from
6# the appropriate directories.
7#
8# This function accepts the following named parameters:
9# DIRS          : one or more directories needed for testing.
10# FILES         : one or more files needed for testing.
11# LIBS          : one or more libraries needed for testing.
12# DIRS_DEST     : specify where the directories should be installed.
13# FILES_DEST    : specify where the files should be installed.
14# LIBS_DEST     : specify where the libraries should be installed.
15# DEV_OBJ_STORE : specify where the actual data files should be placed.
16# DEV_TEST_DIR  : specify the root file for the module test directory.
17# The DEV_OBJ_STORE and DEV_TEST_DIR variables are required.
18
19# The parameters to this function should be set to the list of directories,
20# files, and libraries that need to be installed prior to testing.
21function(android_push_test_files_to_device)
22
23  # The functions in the module need the adb executable.
24  find_program(adb_executable adb)
25  if(NOT adb_executable)
26    message(FATAL_ERROR "could not find adb")
27  endif()
28
29  function(execute_adb_command)
30    execute_process(COMMAND ${adb_executable} ${ARGN} RESULT_VARIABLE res_var OUTPUT_VARIABLE out_var ERROR_VARIABLE err_var)
31    set(out_var ${out_var} PARENT_SCOPE)
32    if(res_var)
33      string(REGEX REPLACE ";" " " com "${ARGN}")
34      message(FATAL_ERROR "Error occurred during adb command: adb ${com}\nError: ${err_var}.")
35    endif()
36  endfunction()
37
38  # Checks to make sure that a given file exists on the device. If it does,
39  # if(file_exists) will return true.
40  macro(check_device_file_exists device_file file_exists)
41    set(${file_exists} "")
42    execute_process(
43      COMMAND ${adb_executable} shell ls ${device_file}
44      OUTPUT_VARIABLE out_var ERROR_VARIABLE out_var)
45    if(NOT out_var) # when a directory exists but is empty the output is empty
46      set(${file_exists} "YES")
47    else()
48      string(FIND ${out_var} "No such file or directory" no_file_exists)
49      if(${no_file_exists} STREQUAL "-1") # -1 means the file exists
50        set(${file_exists} "YES")
51      endif()
52    endif()
53  endmacro()
54
55  # Checks to see if a filename matches a regex.
56  function(filename_regex filename reg_ex)
57    string(REGEX MATCH ${reg_ex} filename_match ${filename})
58    set(filename_match ${filename_match} PARENT_SCOPE)
59  endfunction()
60
61  # If a file with given name exists in the CMAKE_BINARY_DIR then use that file.
62  # Otherwise use the file with root in CMAKE_CURRENT_SOURCE_DIR.
63  macro(set_absolute_path relative_path absolute_path)
64    set(${absolute_path} ${arg_src_dir}/${relative_path})
65    if(EXISTS ${CMAKE_BINARY_DIR}/${relative_path})
66      set(${absolute_path} ${CMAKE_BINARY_DIR}/${relative_path})
67    endif()
68    if(NOT EXISTS ${${absolute_path}})
69      if(EXISTS ${relative_path})
70        set(${absolute_path} ${relative_path})
71      else()
72        message(FATAL_ERROR "Cannot find file for specified path: ${relative_path}")
73      endif()
74    endif()
75  endmacro()
76
77  # This function pushes the data into the device object store and
78  # creates a link to that data file in a specified location.
79  #
80  # This function requires the following un-named parameters:
81  # data_path        : absolute path to data to load into dev obj store.
82  # dev_object_store : absolute path to the device object store directory.
83  # link_origin      : absolute path to the origin of the link to the dev obj store data file.
84  function(push_and_link data_path dev_object_store link_origin)
85    FILE(SHA1 ${data_path} hash_val)
86    set(obj_store_dst ${dev_object_store}/${hash_val})
87    check_device_file_exists(${obj_store_dst} obj_store_file_exists)
88    # TODO: Verify that the object store file is indeed hashed correctly. Could use md5.
89    if(NOT obj_store_file_exists)
90      execute_adb_command(push ${data_path} ${obj_store_dst})
91    endif()
92    check_device_file_exists(${link_origin} link_exists)
93    if(link_exists)
94      execute_adb_command(shell rm -f ${link_origin})
95    endif()
96    foreach(ex ${arg_no_link_regex})
97      filename_regex(${data_path} ${ex})
98      LIST(APPEND match_ex ${filename_match})
99    endforeach()
100    if(match_ex)
101      execute_adb_command(shell cp ${obj_store_dst} ${link_origin})
102    else()
103      execute_adb_command(shell ln -s ${obj_store_dst} ${link_origin})
104    endif()
105  endfunction()
106
107  #----------------------------------------------------------------------------
108  #--------------------Beginning of actual function----------------------------
109  #----------------------------------------------------------------------------
110  set(oneValueArgs FILES_DEST LIBS_DEST DEV_TEST_DIR DEV_OBJ_STORE)
111  set(multiValueArgs FILES LIBS)
112  cmake_parse_arguments(_ptd "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
113
114  # Setup of object store and test dir.
115  check_device_file_exists(${_ptd_DEV_OBJ_STORE} dev_obj_store_exists)
116  if(NOT dev_obj_store_exists)
117    execute_adb_command(shell mkdir -p ${_ptd_DEV_OBJ_STORE})
118  endif()
119  check_device_file_exists(${_ptd_DEV_TEST_DIR} test_dir_exists)
120  if(test_dir_exists)
121    # This is protected in the SetupProjectTests module.
122    execute_adb_command(shell rm -r ${_ptd_DEV_TEST_DIR})
123  endif()
124  execute_adb_command(shell mkdir -p ${_ptd_DEV_TEST_DIR})
125
126  # Looping over the various types of test data possible.
127  foreach(TYPE ${multiValueArgs})
128    if(_ptd_${TYPE})
129
130      # determine if the data type destination has been explicitly specified.
131      if(_ptd_${TYPE}_DEST)
132        set(dest ${_ptd_${TYPE}_DEST})
133      else()
134        if(${TYPE} STREQUAL LIBS)
135          set(dest ${_ptd_DEV_TEST_DIR}/lib)
136        else()
137          set(dest ${_ptd_DEV_TEST_DIR})
138        endif()
139      endif()
140      execute_adb_command(shell mkdir -p ${dest})
141
142      # Loop over the files passed in
143      foreach(relative_path ${_ptd_${TYPE}})
144        # The absolute path can be through the source directory or the build directory.
145        # If the file/dir exists in the build directory that version is chosen.
146        set_absolute_path(${relative_path} absolute_path)
147        # Need to transfer all data files in the data directories to the device
148        # except those explicitly ignored.
149        if(${TYPE} STREQUAL FILES)
150          get_filename_component(file_dir ${relative_path} DIRECTORY)
151          # dest was determined earlier, relative_path is a dir, file is path from relative path to a data
152          set(cur_dest ${dest}/${relative_path})
153          set(on_dev_dir ${dest}/${file_dir})
154          execute_adb_command(shell mkdir -p ${on_dev_dir})
155          if(IS_SYMLINK ${absolute_path})
156            get_filename_component(real_data_origin ${absolute_path} REALPATH)
157            push_and_link(${real_data_origin} ${_ptd_DEV_OBJ_STORE} ${cur_dest})
158          else()
159            push_and_link(${absolute_path} ${_ptd_DEV_OBJ_STORE} ${cur_dest})
160          endif()
161        else() # LIBS
162          execute_adb_command(push ${absolute_path} ${dest})
163        endif()
164      endforeach()
165    endif()
166  endforeach()
167endfunction()
168
169android_push_test_files_to_device(
170  FILES_DEST ${arg_files_dest}
171  LIBS_DEST ${arg_libs_dest}
172  DEV_TEST_DIR ${arg_dev_test_dir}
173  DEV_OBJ_STORE ${arg_dev_obj_store}
174  FILES ${arg_files}
175  LIBS ${arg_libs}
176  )
177