diff --git a/compiler-rt/CMakeLists.txt b/compiler-rt/CMakeLists.txt index e8ba657bbc49c69612292536b5dd7b3c25c93069..ab8bb8f27443a50e10beab105fa9f1e09e1063f5 100644 --- a/compiler-rt/CMakeLists.txt +++ b/compiler-rt/CMakeLists.txt @@ -117,18 +117,36 @@ if ("${COMPILER_RT_DEFAULT_TARGET_TRIPLE}" MATCHES ".*android.*") set(ANDROID_API_LEVEL ${CMAKE_MATCH_2}) endif() +set(COMPILER_RT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) +set(COMPILER_RT_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}) + +# OHOS_LOCAL begin # We define OHOS for ohos targets for now. By default we use devices with hdc connection to test OHOS -# targets. It's also possible to use devices with adb connection which is enabled via ANDROID. +# targets. It's also possible to use devices with adb connection which is enabled via REMOTE_DEVICE_INTERFACE. if (OHOS AND NOT ANDROID) set(OHOS_FAMILY 1) endif() +if (OHOS AND NOT REMOTE_DEVICE_INTERFACE) + set(REMOTE_DEVICE_INTERFACE "hdc") +endif() + +if (ANDROID AND NOT REMOTE_DEVICE_INTERFACE) + set(REMOTE_DEVICE_INTERFACE "adb") +endif() + +if(OHOS) + set(commands_path + "test/sanitizer_common/ohos_family_commands") + configure_file("${COMPILER_RT_SOURCE_DIR}/${commands_path}/ohos_common.py.in" + "${COMPILER_RT_BINARY_DIR}/${commands_path}/ohos_common.py" + @ONLY) +endif() + pythonize_bool(ANDROID) pythonize_bool(OHOS_FAMILY) pythonize_bool(OHOS_HOST) - -set(COMPILER_RT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) -set(COMPILER_RT_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}) +# OHOS_LOCAL end pythonize_bool(LLVM_ENABLE_PER_TARGET_RUNTIME_DIR) diff --git a/compiler-rt/test/lit.common.cfg.py b/compiler-rt/test/lit.common.cfg.py index b30fa67a051509511c5040218b74985f24cf9eff..53a9df0252c656ed9fc11985c9d1cced84050719 100644 --- a/compiler-rt/test/lit.common.cfg.py +++ b/compiler-rt/test/lit.common.cfg.py @@ -491,77 +491,84 @@ else: for vers in min_macos_deployment_target_substitutions: config.substitutions.append( ('%%min_macos_deployment_target=%s.%s' % vers, '') ) -if config.android: - env = os.environ.copy() - if config.android_serial: - env['ANDROID_SERIAL'] = config.android_serial - config.environment['ANDROID_SERIAL'] = config.android_serial +# OHOS_LOCAL begin +remote_cmd = None + +if config.remote_interface == 'hdc': + for var in ( + 'HDC', + 'HDC_SERVER_IP_PORT', + 'HDC_UTID', + ): + if os.environ.get(var): + config.environment[var] = os.environ[var] - adb = os.environ.get('ADB', 'adb') + # import hdc as remote_interface + import remote_interfaces.hdc as remote_interface + config.substitutions.append(('%push_to_device', remote_interface.config_push_str) ) + config.substitutions.append(('%adb_shell ', remote_interface.config_shell_str) ) + config.substitutions.append(('%device_rm', remote_interface.config_remove_str) ) + remote_cmd = remote_interface.hdc_output + push_cmd = remote_interface.push + connect = remote_interface.connect +elif config.remote_interface == 'adb': + os.environ['ANDROID_SERIAL'] = config.android_serial + config.environment['ANDROID_SERIAL'] = config.android_serial + import remote_interfaces.adb as remote_interface + config.substitutions.append( ('%push_to_device', remote_interface.config_push_str ) ) + config.substitutions.append( ('%adb_shell ', remote_interface.config_shell_str ) ) + config.substitutions.append( ('%device_rm', remote_interface.config_remove_str ) ) + remote_cmd = remote_interface.adb_output + push_cmd = remote_interface.push + connect = remote_interface.connect +if config.android: + env = os.environ + import android_commands.android_common as android_common # These are needed for tests to upload/download temp files, such as # suppression-files, to device. - config.substitutions.append( ('%device_rundir/', "/data/local/tmp/Output/") ) - if not config.host_os == 'OHOS': - config.substitutions.append( ('%push_to_device', "%s -s '%s' push " % (adb, env['ANDROID_SERIAL']) ) ) - config.substitutions.append( ('%adb_shell ', "%s -s '%s' shell " % (adb, env['ANDROID_SERIAL']) ) ) - config.substitutions.append( ('%device_rm', "%s -s '%s' shell 'rm ' " % (adb, env['ANDROID_SERIAL']) ) ) - - try: - android_api_level_str = subprocess.check_output([adb, "shell", "getprop", "ro.build.version.sdk"], env=env).rstrip() - android_api_codename = subprocess.check_output([adb, "shell", "getprop", "ro.build.version.codename"], env=env).rstrip().decode("utf-8") - except (subprocess.CalledProcessError, OSError): - lit_config.fatal("Failed to read ro.build.version.sdk (using '%s' as adb)" % adb) - try: - android_api_level = int(android_api_level_str) - except ValueError: - lit_config.fatal("Failed to read ro.build.version.sdk (using '%s' as adb): got '%s'" % (adb, android_api_level_str)) - android_api_level = min(android_api_level, int(config.android_api_level)) - for required in [26, 28, 29, 30]: - if android_api_level >= required: - config.available_features.add('android-%s' % required) - # FIXME: Replace with appropriate version when availible. - if android_api_level > 30 or (android_api_level == 30 and android_api_codename == 'S'): - config.available_features.add('android-thread-properties-api') - else: - config.environment['OHOS_REMOTE_DYN_LINKER'] = os.environ.get('OHOS_REMOTE_DYN_LINKER') + config.substitutions.append( ('%device_rundir/', android_common.ANDROID_TMPDIR) ) + try: + android_api_level_str = remote_cmd(["shell", "getprop", "ro.build.version.sdk"], env=env).rstrip() + android_api_codename = remote_cmd(["shell", "getprop", "ro.build.version.codename"], env=env).rstrip().decode("utf-8") + except (subprocess.CalledProcessError, OSError): + lit_config.fatal("Failed to read ro.build.version.sdk (using '%s' as adb)" % remote_interface.command) + try: + android_api_level = int(android_api_level_str) + except ValueError: + lit_config.fatal("Failed to read ro.build.version.sdk (using '%s' as adb): got '%s'" % + (remote_interface.command, android_api_level_str)) + android_api_level = min(android_api_level, int(config.android_api_level)) + for required in [26, 28, 29, 30]: + if android_api_level >= required: + config.available_features.add('android-%s' % required) + # FIXME: Replace with appropriate version when availible. + if android_api_level > 30 or (android_api_level == 30 and android_api_codename == 'S'): + config.available_features.add('android-thread-properties-api') # Prepare the device. - android_tmpdir = '/data/local/tmp/Output' - subprocess.check_call([adb, "shell", "mkdir", "-p", android_tmpdir], env=env) + remote_cmd(["shell", "mkdir", "-p", android_common.ANDROID_TMPDIR], env=env) for file in config.android_files_to_push: - subprocess.check_call([adb, "push", file, android_tmpdir], env=env) -# OHOS_LOCAL begin + push_cmd(file, android_common.ANDROID_TMPDIR, env=env) elif config.host_os == 'OHOS': for var in [ - 'HDC', - 'HDC_SERVER_IP_PORT', - 'HDC_UTID', 'OHOS_REMOTE_TMP_DIR', 'OHOS_REMOTE_DYN_LINKER', ]: - if var in os.environ: + if os.environ.get(var): config.environment[var] = os.environ[var] - hdc_imp = os.path.join(os.path.dirname(__file__), 'sanitizer_common', 'ohos_family_commands') - sys.path.append(hdc_imp) - import hdc_constants - env = os.environ.copy() + import ohos_common + env = os.environ if config.ohos_host: config.substitutions.append( ('%device_rundir/', "./") ) config.substitutions.append( ('%push_to_device', "echo ") ) config.substitutions.append( ('%adb_shell', "") ) else: - config.substitutions.append( ('%device_rundir/', hdc_constants.TMPDIR) ) - prefix = hdc_constants.get_hdc_cmd_prefix() - prefix_str = ' '.join(prefix) - config.substitutions.append(('%push_to_device', "%s file send " % prefix_str) ) - config.substitutions.append(('%adb_shell ', "%s shell " % prefix_str) ) - config.substitutions.append(('%device_rm', "%s shell 'rm ' " % prefix_str) ) - subprocess.check_call(prefix + ['tconn'], env=env) - subprocess.check_call(prefix + ['shell', 'mkdir', '-p', hdc_constants.TMPDIR], env=env) - + config.substitutions.append( ('%device_rundir/', ohos_common.TMPDIR) ) + connect() + remote_cmd(['shell', 'mkdir', '-p', ohos_common.TMPDIR], env=env) # OHOS_LOCAL end else: config.substitutions.append( ('%device_rundir/', "") ) diff --git a/compiler-rt/test/lit.common.configured.in b/compiler-rt/test/lit.common.configured.in index 27a03dc7bc16ce0358f18f95a201406a66614a23..6987eb0a3be00cc74117c76a112929c3a716a455 100644 --- a/compiler-rt/test/lit.common.configured.in +++ b/compiler-rt/test/lit.common.configured.in @@ -44,6 +44,7 @@ set_default("use_lto", config.use_thinlto) set_default("android", @ANDROID_PYBOOL@) set_default("ohos_family", @OHOS_FAMILY_PYBOOL@) set_default("ohos_host", @OHOS_HOST_PYBOOL@) +set_default("remote_interface", "@REMOTE_DEVICE_INTERFACE@") # OHOS_LOCAL set_default("android_api_level", "@ANDROID_API_LEVEL@") set_default("android_serial", "@ANDROID_SERIAL_FOR_TESTING@") set_default("android_files_to_push", []) @@ -57,6 +58,22 @@ set_default("test_standalone_build_libs", @COMPILER_RT_TEST_STANDALONE_BUILD_LIB set_default("test_suite_supports_overriding_runtime_lib_path", False) config.available_features.add('target-is-%s' % config.target_arch) +# OHOS_LOCAL begin +module_paths = (f'{config.compiler_rt_src_root}/test/sanitizer_common', + f'{config.compiler_rt_obj_root}/test/sanitizer_common/ohos_family_commands') +# we need to update both `sys.path` and `config.environment`, because first will be used during +# lit configure stage, and the second will be used during the run. +config.environment['PYTHONPATH'] = f'{config.environment.get("PYTHONPATH", "")}:{":".join(module_paths)}' +import sys +sys.path.extend(module_paths) + +# empty interface means local run +supported_interfaces = ('hdc', 'adb', '') +if config.remote_interface not in supported_interfaces: + raise RuntimeError(f'{config.remote_interface} is not supported, ' + f'must be one of {supported_interfaces}') +# OHOS_LOCAL end + if config.enable_per_target_runtime_dir: set_default("target_suffix", "") elif config.android: diff --git a/compiler-rt/test/sanitizer_common/android_commands/android_common.py b/compiler-rt/test/sanitizer_common/android_commands/android_common.py index 53e364f8e24bd1dce28cb9dae9a5a4d9d66dffe2..0d2ad627091de4b40528d704e583af3b309f651e 100644 --- a/compiler-rt/test/sanitizer_common/android_commands/android_common.py +++ b/compiler-rt/test/sanitizer_common/android_commands/android_common.py @@ -4,15 +4,22 @@ import time ANDROID_TMPDIR = '/data/local/tmp/Output' ADB = os.environ.get('ADB', 'adb') DYN_LINKER = os.environ.get('OHOS_REMOTE_DYN_LINKER') # OHOS_LOCAL +ANDROID_SERIAL = os.environ.get('ANDROID_SERIAL') # OHOS_LOCAL verbose = False if os.environ.get('ANDROID_RUN_VERBOSE') == '1': verbose = True -def host_to_device_path(path): +# OHOS_LOCAL begin +def get_adb_cmd_prefix(): + device = ['-s', ANDROID_SERIAL] if ANDROID_SERIAL else [] + return [ADB, *device] + +def host_to_device_path(path, device_tmpdir=ANDROID_TMPDIR): rel = os.path.relpath(path, "/") - dev = os.path.join(ANDROID_TMPDIR, rel) + dev = os.path.join(device_tmpdir, rel) return dev +# OHOS_LOCAL end def adb(args, attempts = 1, timeout_sec = 600): if verbose: @@ -22,7 +29,11 @@ def adb(args, attempts = 1, timeout_sec = 600): ret = 255 while attempts > 0 and ret != 0: attempts -= 1 - ret = subprocess.call(['timeout', str(timeout_sec), ADB] + args, stdout=out, stderr=subprocess.STDOUT) + # OHOS_LOCAL begin + ret = subprocess.call( + ['timeout', str(timeout_sec)] + get_adb_cmd_prefix() + args, + stdout=out, stderr=subprocess.STDOUT) + # OHOS_LOCAL end if ret != 0: print("adb command failed", args) print(tmpname) @@ -40,6 +51,8 @@ def pull_from_device(path): os.unlink(tmp) return text -def push_to_device(path): - dst_path = host_to_device_path(path) +# OHOS_LOCAL begin +def push_to_device(path, device_tmpdir=ANDROID_TMPDIR): + dst_path = host_to_device_path(path, device_tmpdir) adb(['push', path, dst_path], 5, 60) +# OHOS_LOCAL end diff --git a/compiler-rt/test/sanitizer_common/ohos_family_commands/hdc_constants.py b/compiler-rt/test/sanitizer_common/ohos_family_commands/hdc_constants.py deleted file mode 100644 index 3ea67a67c65e246992f0294f9f9ef96075a619f7..0000000000000000000000000000000000000000 --- a/compiler-rt/test/sanitizer_common/ohos_family_commands/hdc_constants.py +++ /dev/null @@ -1,16 +0,0 @@ - -import os - -# TODO: move this to the cmake ? -HDC = os.environ.get('HDC', 'hdc') -# Please set "HDC_SERVER_IP_PORT" and "HDC_UTID" environment variables -# to pass options for connecting to remote device if needed -HDC_SERVER_IP_PORT = os.environ.get('HDC_SERVER_IP_PORT') -HDC_UTID = os.environ.get('HDC_UTID') -TMPDIR = os.environ.get('OHOS_REMOTE_TMP_DIR', '/data/local/tmp/Output') -DYN_LINKER = os.environ.get('OHOS_REMOTE_DYN_LINKER') - -def get_hdc_cmd_prefix(): - server = ['-s', HDC_SERVER_IP_PORT] if HDC_SERVER_IP_PORT else [] - device = ['-t', HDC_UTID] if HDC_UTID else [] - return [HDC, *server, *device] diff --git a/compiler-rt/test/sanitizer_common/ohos_family_commands/ohos_common.py b/compiler-rt/test/sanitizer_common/ohos_family_commands/ohos_common.py.in similarity index 54% rename from compiler-rt/test/sanitizer_common/ohos_family_commands/ohos_common.py rename to compiler-rt/test/sanitizer_common/ohos_family_commands/ohos_common.py.in index 461fae43540f2d9c86134e6ba920583010931414..c010b35b3ee73eddf041f8c798552dc2d5293a2b 100755 --- a/compiler-rt/test/sanitizer_common/ohos_family_commands/ohos_common.py +++ b/compiler-rt/test/sanitizer_common/ohos_family_commands/ohos_common.py.in @@ -1,68 +1,20 @@ #!/usr/bin/env python3 -import os, subprocess, tempfile, re -import hdc_constants +import os +import re -verbose = False -if os.environ.get('HOS_RUN_VERBOSE') == '1': - verbose = True +from remote_interfaces.@REMOTE_DEVICE_INTERFACE@ import * -def host_to_device_path(path): - rel = os.path.relpath(path, "/") - dev = os.path.join(hdc_constants.TMPDIR, rel) - return dev +os.environ['RUN_VERBOSE'] = os.environ.get('OHOS_RUN_VERBOSE', '0') -def hdc_output(args): - command = hdc_constants.get_hdc_cmd_prefix() + args - if verbose: - print ("[CMD]:" + " ".join(command)) - return subprocess.check_output(command, stderr=subprocess.STDOUT, timeout=300) - -def hdc(args, attempts=1, check_stdout=''): - tmpname = tempfile.mktemp() - out = open(tmpname, 'w') - ret = 255 - while attempts > 0 and ret != 0: - attempts -= 1 - ret = 0 - output = hdc_output(args) - # hdc exit code is always zero - if check_stdout not in output.decode(): - ret = 255 - if ret != 0: - print ("hdc command failed", args) - print (tmpname) - out.close() - out = open(tmpname, 'r') - print (out.read()) - out.close() - os.unlink(tmpname) - return ret - -def pull_from_device(path): - # hdc can't download empty files - file_sz = hdc_output(['shell', 'du', path]).split() - if file_sz and file_sz[0] == b'0': - return '' - - tmp = tempfile.mktemp() - hdc(['file', 'recv', path, tmp], attempts=5, check_stdout='FileTransfer finish') - text = open(tmp, 'r').read() - os.unlink(tmp) - return text - -def push_to_device(path): - dst_path = host_to_device_path(path) - # hdc do not auto create directories on device - hdc(['shell', 'mkdir', '-p', os.path.dirname(dst_path)]) - hdc(['file', 'send', path, dst_path], attempts=5, check_stdout='FileTransfer finish') - hdc(['shell', 'chmod', '+x', dst_path]) +TMPDIR = os.environ.get('OHOS_REMOTE_TMP_DIR', '/data/local/tmp/Output') +DYN_LINKER = os.environ.get('OHOS_REMOTE_DYN_LINKER') def map_path(path, do_push): if os.path.exists(path): if do_push: - push_to_device(path) - return host_to_device_path(path) + push_to_device(path, TMPDIR) + return host_to_device_path(path, TMPDIR) return path def map_list(value, sep, regex, get_path_and_do_push): @@ -92,9 +44,9 @@ def build_env(do_push=True): # set them by default to llvm-symbolizer symb_name = '%s_SYMBOLIZER_PATH' % san args.append('%s=%s' % (symb_name, os.environ.get('LLVM_SYMBOLIZER_PATH', - os.path.join(hdc_constants.TMPDIR,'llvm-symbolizer-aarch64')))) + os.path.join(TMPDIR,'llvm-symbolizer-aarch64')))) # HOS linker ignores RPATH. Set LD_LIBRARY_PATH to Output dir. - args.append('LD_LIBRARY_PATH=%s' % ( hdc_constants.TMPDIR,)) + args.append('LD_LIBRARY_PATH=%s' % ( TMPDIR,)) for (key, value) in os.environ.items(): san_opt = key.endswith('SAN_OPTIONS') if san_opt and set_abort_on_error: @@ -123,4 +75,4 @@ def get_output_from_args(args): elif arg == '-o': output = args.pop(0) - return output, output_type \ No newline at end of file + return output, output_type diff --git a/compiler-rt/test/sanitizer_common/ohos_family_commands/ohos_compile.py b/compiler-rt/test/sanitizer_common/ohos_family_commands/ohos_compile.py index 8e2f92c35284ca264491ed22dcf446b94cc9d4f4..fdea91ae4a938eed470471d48e52882669e1917e 100755 --- a/compiler-rt/test/sanitizer_common/ohos_family_commands/ohos_compile.py +++ b/compiler-rt/test/sanitizer_common/ohos_family_commands/ohos_compile.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 -import os, sys, subprocess +import os, subprocess +import sys from ohos_common import * - here = os.path.abspath(os.path.dirname(sys.argv[0])) hos_run = os.path.join(here, 'ohos_run.py') @@ -14,8 +14,8 @@ if output == None: sys.exit(1) append_args = [] -if hdc_constants.DYN_LINKER: - append_args.append('-Wl,--dynamic-linker=' + hdc_constants.DYN_LINKER) +if DYN_LINKER: + append_args.append('-Wl,--dynamic-linker=' + DYN_LINKER) ret = subprocess.call(sys.argv[1:] + append_args) @@ -23,7 +23,7 @@ if ret != 0: sys.exit(ret) if output_type in ['executable', 'shared']: - push_to_device(output) + push_to_device(output, TMPDIR) if output_type == 'executable': os.rename(output, output + '.real') diff --git a/compiler-rt/test/sanitizer_common/ohos_family_commands/ohos_host_compile.py b/compiler-rt/test/sanitizer_common/ohos_family_commands/ohos_host_compile.py index c4dbe0523dde2f1c51721b52023ea062ccb5ecc3..b8b3bb3ea82586438c59d41b8fd08a254ec48835 100755 --- a/compiler-rt/test/sanitizer_common/ohos_family_commands/ohos_host_compile.py +++ b/compiler-rt/test/sanitizer_common/ohos_family_commands/ohos_host_compile.py @@ -13,8 +13,8 @@ if output == None: sys.exit(1) append_args = [] -if hdc_constants.DYN_LINKER: - append_args.append('-Wl,--dynamic-linker=' + hdc_constants.DYN_LINKER) +if DYN_LINKER: + append_args.append('-Wl,--dynamic-linker=' + DYN_LINKER) # TODO: Fix adding compiler-rt include sanitizer_include = os.path.abspath(os.path.join(here, "../../../include/")) diff --git a/compiler-rt/test/sanitizer_common/ohos_family_commands/ohos_run.py b/compiler-rt/test/sanitizer_common/ohos_family_commands/ohos_run.py index 136d46c3a304610b26fedb9fb7226f052f794cdf..164a7bf488371878a1ac71e515c3210a643c4ca6 100755 --- a/compiler-rt/test/sanitizer_common/ohos_family_commands/ohos_run.py +++ b/compiler-rt/test/sanitizer_common/ohos_family_commands/ohos_run.py @@ -1,10 +1,9 @@ #!/usr/bin/env python3 -import os, signal, sys, subprocess -import re +import os, signal, sys from ohos_common import * -device_binary = host_to_device_path(sys.argv[0]) +device_binary = host_to_device_path(sys.argv[0], TMPDIR) device_env = build_env() device_args = ' '.join(sys.argv[1:]) # FIXME: escape? device_stdout = device_binary + '.stdout' @@ -15,9 +14,9 @@ device_linker = '' # Currently OHOS set log_path in UBSAN_OPTIONS # Tests expects to see output in stdout/stderr and fails when it is not there # So unset UBSAN_OPTIONS before run tests. -hdc(['shell', 'unset UBSAN_OPTIONS && cd %s && %s %s %s %s >%s 2>%s ; echo $? >%s' % - (hdc_constants.TMPDIR, device_env, device_linker, device_binary, device_args, - device_stdout, device_stderr, device_exitcode)]) +remote(['shell', 'unset UBSAN_OPTIONS && cd %s && %s %s %s %s >%s 2>%s ; echo $? >%s' % + (TMPDIR, device_env, device_linker, device_binary, device_args, + device_stdout, device_stderr, device_exitcode)]) sys.stdout.write(pull_from_device(device_stdout)) sys.stderr.write(pull_from_device(device_stderr)) diff --git a/compiler-rt/test/sanitizer_common/remote_interfaces/adb.py b/compiler-rt/test/sanitizer_common/remote_interfaces/adb.py new file mode 100644 index 0000000000000000000000000000000000000000..91349f48cb4f49c7b92bf7ce15acd9bde4e69781 --- /dev/null +++ b/compiler-rt/test/sanitizer_common/remote_interfaces/adb.py @@ -0,0 +1,20 @@ +from android_commands.android_common import * + +import subprocess + +def adb_output(args, timeout_sec=300, env=None): + return subprocess.check_output(get_adb_cmd_prefix() + args, + stderr=subprocess.STDOUT, + timeout=timeout_sec, + env=env) + +def connect(): + pass + +remote = adb +push = push_to_device +command = ADB +adb_str_prefix = ' '.join(get_adb_cmd_prefix()) +config_push_str = f'{adb_str_prefix} push ' +config_remove_str = f'{adb_str_prefix} shell rm ' +config_shell_str = f'{adb_str_prefix} shell ' diff --git a/compiler-rt/test/sanitizer_common/remote_interfaces/hdc.py b/compiler-rt/test/sanitizer_common/remote_interfaces/hdc.py new file mode 100644 index 0000000000000000000000000000000000000000..c99f73b77cc1b9c4ba54d85605afd41d4268d00e --- /dev/null +++ b/compiler-rt/test/sanitizer_common/remote_interfaces/hdc.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python3 + +import os, subprocess, tempfile + +# TODO: move this to the cmake ? +HDC = os.environ.get('HDC', 'hdc') +# Please set "HDC_SERVER_IP_PORT" and "HDC_UTID" environment variables +# to pass options for connecting to remote device if needed +HDC_SERVER_IP_PORT = os.environ.get('HDC_SERVER_IP_PORT') +HDC_UTID = os.environ.get('HDC_UTID') + +def get_hdc_cmd_prefix(): + server = ['-s', HDC_SERVER_IP_PORT] if HDC_SERVER_IP_PORT else [] + device = ['-t', HDC_UTID] if HDC_UTID else [] + return [HDC, *server, *device] + +def verbose(): + return os.environ.get('RUN_VERBOSE') == '1' + +def host_to_device_path(path, device_tmpdir): + rel = os.path.relpath(path, "/") + dev = os.path.join(device_tmpdir, rel) + return dev + +def hdc_output(args, timeout=300, env=None): + command = get_hdc_cmd_prefix() + args + if verbose(): + print ("[CMD]:" + " ".join(command)) + return subprocess.check_output( + command, + stderr=subprocess.STDOUT, + timeout=timeout, + env=env) + +def hdc(args, attempts=1, check_stdout='', env=None): + tmpname = tempfile.mktemp() + out = open(tmpname, 'w') + ret = 255 + while attempts > 0 and ret != 0: + attempts -= 1 + ret = 0 + output = hdc_output(args, env=env) + # hdc exit code is always zero + if check_stdout not in output.decode(): + ret = 255 + if ret != 0: + print ("hdc command failed", args) + print (tmpname) + out.close() + out = open(tmpname, 'r') + print (out.read()) + out.close() + os.unlink(tmpname) + return ret + +def pull_from_device(path): + # hdc can't download empty files + file_sz = hdc_output(['shell', 'du', path]).split() + if file_sz and file_sz[0] == b'0': + return '' + + tmp = tempfile.mktemp() + hdc(['file', 'recv', path, tmp], attempts=5, check_stdout='FileTransfer finish') + text = open(tmp, 'r').read() + os.unlink(tmp) + return text + +def _do_push(src, dst, env=None): + hdc(['file', 'send', src, dst], + attempts=5, + check_stdout='FileTransfer finish', + env=env) + +def push_to_device(path, device_tmpdir): + dst_path = host_to_device_path(path, device_tmpdir) + # hdc do not auto create directories on device + hdc(['shell', 'mkdir', '-p', os.path.dirname(dst_path)]) + _do_push(path, dst_path) + hdc(['shell', 'chmod', '+x', dst_path]) + +def connect(): + hdc(['tconn']) + +remote = hdc +push = _do_push +command = HDC +hdc_str_prefix = ' '.join(get_hdc_cmd_prefix()) +config_push_str = f'{hdc_str_prefix} file send ' +config_remove_str = f'{hdc_str_prefix} shell rm ' +config_shell_str = f'{hdc_str_prefix} shell '