Android tooling: host/target path distinction
Introduce host and target path separation in the
Android incremental_install.py. This will allow
running this script (and use bazel mobile-install)
on platforms with non-POSIX path semantics
(e.g. Windows).
See https://github.com/bazelbuild/bazel/issues/3264
Change-Id: If6ec09f100dd2e0be3389dce25cb1a13305226e9
PiperOrigin-RevId: 160531950
diff --git a/tools/android/incremental_install.py b/tools/android/incremental_install.py
index b02d722..bd52880 100644
--- a/tools/android/incremental_install.py
+++ b/tools/android/incremental_install.py
@@ -1,3 +1,4 @@
+# pylint: disable=g-bad-file-header
# Copyright 2015 The Bazel Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,6 +19,7 @@
import hashlib
import logging
import os
+import posixpath
import re
import shutil
import subprocess
@@ -111,6 +113,10 @@
"""Raised when the SDK on the target device is older than the app allows."""
+hostpath = os.path
+targetpath = posixpath
+
+
class Adb(object):
"""A class to handle interaction with adb."""
@@ -174,7 +180,7 @@
def _CreateLocalFile(self):
"""Returns a path to a temporary local file in the temp directory."""
- local = os.path.join(self._temp_dir, "adbfile_%d" % self._file_counter)
+ local = hostpath.join(self._temp_dir, "adbfile_%d" % self._file_counter)
self._file_counter += 1
return local
@@ -336,14 +342,14 @@
"""
# Fetch the manifest on the device
- dex_dir = os.path.join(app_dir, "dex")
+ dex_dir = targetpath.join(app_dir, "dex")
adb.Mkdir(dex_dir)
old_manifest = None
if not full_install:
logging.info("Fetching dex manifest from device...")
- old_manifest_contents = adb.Pull("%s/manifest" % dex_dir)
+ old_manifest_contents = adb.Pull(targetpath.join(dex_dir, "manifest"))
if old_manifest_contents:
old_manifest = ParseManifest(old_manifest_contents)
else:
@@ -354,7 +360,7 @@
# was interrupted. Wipe the slate clean. Do this also in case we do a full
# installation.
old_manifest = {}
- adb.Delete("%s/*" % dex_dir)
+ adb.Delete(targetpath.join(dex_dir, "*"))
new_manifest = ParseManifest(dexmanifest)
dexes_to_delete = set(old_manifest) - set(new_manifest)
@@ -373,7 +379,7 @@
# Delete the manifest so that we know how to get back to a consistent state
# if we are interrupted.
- adb.Delete("%s/manifest" % dex_dir)
+ adb.Delete(targetpath.join(dex_dir, "manifest"))
# Tuple of (local, remote) files to push to the device.
files_to_push = []
@@ -385,26 +391,26 @@
for i, dexzip_name in enumerate(dexzips_in_upload):
zip_dexes = [
d for d in dexes_to_upload if new_manifest[d].input_file == dexzip_name]
- dexzip_tempdir = os.path.join(temp_dir, "dex", str(i))
- with zipfile.ZipFile(os.path.join(execroot, dexzip_name)) as dexzip:
+ dexzip_tempdir = hostpath.join(temp_dir, "dex", str(i))
+ with zipfile.ZipFile(hostpath.join(execroot, dexzip_name)) as dexzip:
for dex in zip_dexes:
zippath = new_manifest[dex].zippath
dexzip.extract(zippath, dexzip_tempdir)
- files_to_push.append(
- (os.path.join(dexzip_tempdir, zippath), "%s/%s" % (dex_dir, dex)))
+ files_to_push.append((hostpath.join(dexzip_tempdir, zippath),
+ targetpath.join(dex_dir, dex)))
# Now gather all the dexes that are not within a .zip file.
dexes_to_upload = set(
d for d in dexes_to_upload if new_manifest[d].zippath == "-")
for dex in dexes_to_upload:
- files_to_push.append(
- (new_manifest[dex].input_file, "%s/%s" % (dex_dir, dex)))
+ files_to_push.append((new_manifest[dex].input_file, targetpath.join(
+ dex_dir, dex)))
num_files = len(dexes_to_delete) + len(files_to_push)
logging.info("Updating %d dex%s...", num_files, "es" if num_files > 1 else "")
# Delete the dexes that are not in the new manifest
- adb.DeleteMultiple(os.path.join(dex_dir, dex) for dex in dexes_to_delete)
+ adb.DeleteMultiple(targetpath.join(dex_dir, dex) for dex in dexes_to_delete)
# Upload all the files.
upload_walltime_start = time.time()
@@ -426,7 +432,7 @@
# If no dex upload failed, upload the manifest. If any upload failed, the
# exception should have been re-raised above.
# Call result() to raise the exception if there was one.
- adb.PushString(dexmanifest, "%s/manifest" % dex_dir).result()
+ adb.PushString(dexmanifest, targetpath.join(dex_dir, "manifest")).result()
def Checksum(filename):
@@ -459,7 +465,7 @@
new_checksum = Checksum(resource_apk)
# Fetch the checksum of the resources file on the device, if it exists
- device_checksum_file = "%s/%s" % (app_dir, "resources_checksum")
+ device_checksum_file = targetpath.join(app_dir, "resources_checksum")
old_checksum = adb.Pull(device_checksum_file)
if old_checksum == new_checksum:
logging.info("Application resources up-to-date")
@@ -469,7 +475,7 @@
# Remove the checksum file on the device so that if the transfer is
# interrupted, we know how to get the device back to a consistent state.
adb.Delete(device_checksum_file)
- adb.Push(resource_apk, "%s/%s" % (app_dir, "resources.ap_")).result()
+ adb.Push(resource_apk, targetpath.join(app_dir, "resources.ap_")).result()
# Write the new checksum to the device.
adb.PushString(new_checksum, device_checksum_file).result()
@@ -524,13 +530,14 @@
device_manifest = None
if not full_install:
- device_manifest = adb.Pull("%s/native/native_manifest" % app_dir)
+ device_manifest = adb.Pull(
+ targetpath.join(app_dir, "native", "native_manifest"))
device_checksums = {}
if device_manifest is None:
# If we couldn't fetch the device manifest or if this is a non-incremental
# install, wipe the slate clean
- adb.Delete("%s/native" % app_dir)
+ adb.Delete(targetpath.join(app_dir, "native"))
else:
# Otherwise, parse the manifest. Note that this branch is also taken if the
# manifest is empty.
@@ -545,8 +552,8 @@
libs_to_upload.update([l for l in common_libs
if install_checksums[l] != device_checksums[l]])
- libs_to_push = [(basename_to_path[lib], "%s/native/%s" % (app_dir, lib))
- for lib in libs_to_upload]
+ libs_to_push = [(basename_to_path[lib], targetpath.join(
+ app_dir, "native", lib)) for lib in libs_to_upload]
if not libs_to_delete and not libs_to_push and device_manifest is not None:
logging.info("Native libs up-to-date")
@@ -556,11 +563,11 @@
logging.info("Updating %d native lib%s...",
num_files, "s" if num_files != 1 else "")
- adb.Delete("%s/native/native_manifest" % app_dir)
+ adb.Delete(targetpath.join(app_dir, "native", "native_manifest"))
if libs_to_delete:
- adb.DeleteMultiple([
- "%s/native/%s" % (app_dir, lib) for lib in libs_to_delete])
+ adb.DeleteMultiple(
+ [targetpath.join(app_dir, "native", lib) for lib in libs_to_delete])
upload_walltime_start = time.time()
fs = [adb.Push(local, remote) for local, remote in libs_to_push]
@@ -581,13 +588,14 @@
install_manifest = [
name + " " + checksum for name, checksum in install_checksums.iteritems()]
adb.PushString("\n".join(install_manifest),
- "%s/native/native_manifest" % app_dir).result()
+ targetpath.join(app_dir, "native",
+ "native_manifest")).result()
def VerifyInstallTimestamp(adb, app_package):
"""Verifies that the app is unchanged since the last mobile-install."""
- expected_timestamp = adb.Pull("%s/%s/install_timestamp" % (
- DEVICE_DIRECTORY, app_package))
+ expected_timestamp = adb.Pull(
+ targetpath.join(DEVICE_DIRECTORY, app_package, "install_timestamp"))
if not expected_timestamp:
raise TimestampException(
"Cannot verify last mobile install. At least one non-incremental "
@@ -609,10 +617,10 @@
def SplitIncrementalInstall(adb, app_package, execroot, split_main_apk,
split_apks):
"""Does incremental installation using split packages."""
- app_dir = os.path.join(DEVICE_DIRECTORY, app_package)
- device_manifest_path = "%s/split_manifest" % app_dir
+ app_dir = targetpath.join(DEVICE_DIRECTORY, app_package)
+ device_manifest_path = targetpath.join(app_dir, "split_manifest")
device_manifest = adb.Pull(device_manifest_path)
- expected_timestamp = adb.Pull("%s/install_timestamp" % app_dir)
+ expected_timestamp = adb.Pull(targetpath.join(app_dir, "install_timestamp"))
actual_timestamp = adb.GetInstallTime(app_package)
device_checksums = {}
if device_manifest is not None:
@@ -623,9 +631,9 @@
install_checksums = {}
install_checksums["__MAIN__"] = Checksum(
- os.path.join(execroot, split_main_apk))
+ hostpath.join(execroot, split_main_apk))
for apk in split_apks:
- install_checksums[apk] = Checksum(os.path.join(execroot, apk))
+ install_checksums[apk] = Checksum(hostpath.join(execroot, apk))
reinstall_main = False
if (device_manifest is None or actual_timestamp is None or
@@ -655,20 +663,20 @@
if reinstall_main:
logging.info("Installing main APK...")
adb.Uninstall(app_package)
- adb.InstallMultiple(os.path.join(execroot, split_main_apk))
+ adb.InstallMultiple(targetpath.join(execroot, split_main_apk))
adb.PushString(
adb.GetInstallTime(app_package),
- "%s/install_timestamp" % app_dir).result()
+ targetpath.join(app_dir, "install_timestamp")).result()
logging.info("Reinstalling %s APKs...", len(apks_to_update))
for apk in apks_to_update:
- adb.InstallMultiple(os.path.join(execroot, apk), app_package)
+ adb.InstallMultiple(targetpath.join(execroot, apk), app_package)
install_manifest = [
name + " " + checksum for name, checksum in install_checksums.iteritems()]
adb.PushString("\n".join(install_manifest),
- "%s/split_manifest" % app_dir).result()
+ targetpath.join(app_dir, "split_manifest")).result()
def IncrementalInstall(adb_path, execroot, stub_datafile, output_marker,
@@ -697,8 +705,8 @@
temp_dir = tempfile.mkdtemp()
try:
adb = Adb(adb_path, temp_dir, adb_jobs, user_home_dir)
- app_package = GetAppPackage(os.path.join(execroot, stub_datafile))
- app_dir = os.path.join(DEVICE_DIRECTORY, app_package)
+ app_package = GetAppPackage(hostpath.join(execroot, stub_datafile))
+ app_dir = targetpath.join(DEVICE_DIRECTORY, app_package)
if split_main_apk:
SplitIncrementalInstall(adb, app_package, execroot, split_main_apk,
split_apks)
@@ -706,20 +714,20 @@
if not apk:
VerifyInstallTimestamp(adb, app_package)
- with file(os.path.join(execroot, dexmanifest)) as f:
+ with file(hostpath.join(execroot, dexmanifest)) as f:
dexmanifest = f.read()
UploadDexes(adb, execroot, app_dir, temp_dir, dexmanifest, bool(apk))
# TODO(ahumesky): UploadDexes waits for all the dexes to be uploaded, and
# then UploadResources is called. We could instead enqueue everything
# onto the threadpool so that uploading resources happens sooner.
- UploadResources(adb, os.path.join(execroot, resource_apk), app_dir)
+ UploadResources(adb, hostpath.join(execroot, resource_apk), app_dir)
UploadNativeLibs(adb, native_libs, app_dir, bool(apk))
if apk:
- apk_path = os.path.join(execroot, apk)
+ apk_path = targetpath.join(execroot, apk)
adb.Install(apk_path)
future = adb.PushString(
adb.GetInstallTime(app_package),
- "%s/%s/install_timestamp" % (DEVICE_DIRECTORY, app_package))
+ targetpath.join(DEVICE_DIRECTORY, app_package, "install_timestamp"))
future.result()
else:
if start_type == "warm":