Add --sync support to push.

Passing --sync only copies files that are older on the device.

Test: nose2
Bug: None
Change-Id: I2ff6c3d1fe29262c8ee50db316aa92fc38dd7147
This commit is contained in:
Dan Albert 2017-05-18 22:56:48 -07:00
parent def4aae26d
commit 06b0d6b01b
4 changed files with 70 additions and 27 deletions

View File

@ -126,8 +126,9 @@ static void help() {
" reverse --remove-all remove all reverse socket connections from device\n"
"\n"
"file transfer:\n"
" push LOCAL... REMOTE\n"
" push [--sync] LOCAL... REMOTE\n"
" copy local files/directories to device\n"
" --sync: only push files that are newer on the host than the device\n"
" pull [-a] REMOTE... LOCAL\n"
" copy files/dirs from device\n"
" -a: preserve file timestamp and mode\n"
@ -1233,9 +1234,8 @@ static int restore(int argc, const char** argv) {
return 0;
}
static void parse_push_pull_args(const char** arg, int narg,
std::vector<const char*>* srcs,
const char** dst, bool* copy_attrs) {
static void parse_push_pull_args(const char** arg, int narg, std::vector<const char*>* srcs,
const char** dst, bool* copy_attrs, bool* sync) {
*copy_attrs = false;
srcs->clear();
@ -1248,6 +1248,10 @@ static void parse_push_pull_args(const char** arg, int narg,
// Silently ignore for backwards compatibility.
} else if (!strcmp(*arg, "-a")) {
*copy_attrs = true;
} else if (!strcmp(*arg, "--sync")) {
if (sync != nullptr) {
*sync = true;
}
} else if (!strcmp(*arg, "--")) {
ignore_flags = true;
} else {
@ -1654,19 +1658,20 @@ int adb_commandline(int argc, const char** argv) {
}
else if (!strcmp(argv[0], "push")) {
bool copy_attrs = false;
bool sync = false;
std::vector<const char*> srcs;
const char* dst = nullptr;
parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, &copy_attrs);
parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, &copy_attrs, &sync);
if (srcs.empty() || !dst) return syntax_error("push requires an argument");
return do_sync_push(srcs, dst) ? 0 : 1;
return do_sync_push(srcs, dst, sync) ? 0 : 1;
}
else if (!strcmp(argv[0], "pull")) {
bool copy_attrs = false;
std::vector<const char*> srcs;
const char* dst = ".";
parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, &copy_attrs);
parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, &copy_attrs, nullptr);
if (srcs.empty()) return syntax_error("pull requires an argument");
return do_sync_pull(srcs, dst, copy_attrs) ? 0 : 1;
}
@ -2086,7 +2091,7 @@ static int install_app_legacy(TransportType transport, const char* serial, int a
std::vector<const char*> apk_file = {argv[last_apk]};
std::string apk_dest = android::base::StringPrintf(
where, android::base::Basename(argv[last_apk]).c_str());
if (!do_sync_push(apk_file, apk_dest.c_str())) goto cleanup_apk;
if (!do_sync_push(apk_file, apk_dest.c_str(), false)) goto cleanup_apk;
argv[last_apk] = apk_dest.c_str(); /* destination name, not source location */
result = pm_command(transport, serial, argc, argv);

View File

@ -674,11 +674,22 @@ static bool sync_stat_fallback(SyncConnection& sc, const char* path, struct stat
return true;
}
static bool sync_send(SyncConnection& sc, const char* lpath, const char* rpath,
unsigned mtime, mode_t mode)
{
static bool sync_send(SyncConnection& sc, const char* lpath, const char* rpath, unsigned mtime,
mode_t mode, bool sync) {
std::string path_and_mode = android::base::StringPrintf("%s,%d", rpath, mode);
if (sync) {
struct stat st;
if (sync_lstat(sc, rpath, &st)) {
// For links, we cannot update the atime/mtime.
if ((S_ISREG(mode & st.st_mode) && st.st_mtime == static_cast<time_t>(mtime)) ||
(S_ISLNK(mode & st.st_mode) && st.st_mtime >= static_cast<time_t>(mtime))) {
sc.RecordFilesSkipped(1);
return true;
}
}
}
if (S_ISLNK(mode)) {
#if !defined(_WIN32)
char buf[PATH_MAX];
@ -902,7 +913,7 @@ static bool copy_local_dir_remote(SyncConnection& sc, std::string lpath,
if (list_only) {
sc.Println("would push: %s -> %s", ci.lpath.c_str(), ci.rpath.c_str());
} else {
if (!sync_send(sc, ci.lpath.c_str(), ci.rpath.c_str(), ci.time, ci.mode)) {
if (!sync_send(sc, ci.lpath.c_str(), ci.rpath.c_str(), ci.time, ci.mode, false)) {
return false;
}
}
@ -916,7 +927,7 @@ static bool copy_local_dir_remote(SyncConnection& sc, std::string lpath,
return true;
}
bool do_sync_push(const std::vector<const char*>& srcs, const char* dst) {
bool do_sync_push(const std::vector<const char*>& srcs, const char* dst, bool sync) {
SyncConnection sc;
if (!sc.IsValid()) return false;
@ -981,7 +992,7 @@ bool do_sync_push(const std::vector<const char*>& srcs, const char* dst) {
dst_dir.append(android::base::Basename(src_path));
}
success &= copy_local_dir_remote(sc, src_path, dst_dir.c_str(), false, false);
success &= copy_local_dir_remote(sc, src_path, dst_dir.c_str(), sync, false);
continue;
} else if (!should_push_file(st.st_mode)) {
sc.Warning("skipping special file '%s' (mode = 0o%o)", src_path, st.st_mode);
@ -1002,7 +1013,7 @@ bool do_sync_push(const std::vector<const char*>& srcs, const char* dst) {
sc.NewTransfer();
sc.SetExpectedTotalBytes(st.st_size);
success &= sync_send(sc, src_path, dst_path, st.st_mtime, st.st_mode);
success &= sync_send(sc, src_path, dst_path, st.st_mtime, st.st_mode, sync);
sc.ReportTransferRate(src_path, TransferDirection::push);
}

View File

@ -81,7 +81,7 @@ union syncmsg {
void file_sync_service(int fd, void* cookie);
bool do_sync_ls(const char* path);
bool do_sync_push(const std::vector<const char*>& srcs, const char* dst);
bool do_sync_push(const std::vector<const char*>& srcs, const char* dst, bool sync);
bool do_sync_pull(const std::vector<const char*>& srcs, const char* dst,
bool copy_attrs, const char* name=nullptr);

View File

@ -1130,8 +1130,18 @@ class FileOperationsTest(DeviceTest):
if host_dir is not None:
shutil.rmtree(host_dir)
def verify_sync(self, device, temp_files, device_dir):
"""Verifies that a list of temp files was synced to the device."""
# Confirm that every file on the device mirrors that on the host.
for temp_file in temp_files:
device_full_path = posixpath.join(
device_dir, temp_file.base_name)
dev_md5, _ = device.shell(
[get_md5_prog(self.device), device_full_path])[0].split()
self.assertEqual(temp_file.checksum, dev_md5)
def test_sync(self):
"""Sync a randomly generated directory of files to specified device."""
"""Sync a host directory to the data partition."""
try:
base_dir = tempfile.mkdtemp()
@ -1141,9 +1151,10 @@ class FileOperationsTest(DeviceTest):
os.makedirs(full_dir_path)
# Create 32 random files within the host mirror.
temp_files = make_random_host_files(in_dir=full_dir_path, num_files=32)
temp_files = make_random_host_files(
in_dir=full_dir_path, num_files=32)
# Clean up any trash on the device.
# Clean up any stale files on the device.
device = adb.get_device() # pylint: disable=no-member
device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
@ -1155,19 +1166,35 @@ class FileOperationsTest(DeviceTest):
else:
os.environ['ANDROID_PRODUCT_OUT'] = old_product_out
# Confirm that every file on the device mirrors that on the host.
for temp_file in temp_files:
device_full_path = posixpath.join(self.DEVICE_TEMP_DIR,
temp_file.base_name)
dev_md5, _ = device.shell(
[get_md5_prog(self.device), device_full_path])[0].split()
self.assertEqual(temp_file.checksum, dev_md5)
self.verify_sync(device, temp_files, self.DEVICE_TEMP_DIR)
self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
#self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
finally:
if base_dir is not None:
shutil.rmtree(base_dir)
def test_push_sync(self):
"""Sync a host directory to a specific path."""
try:
temp_dir = tempfile.mkdtemp()
temp_files = make_random_host_files(in_dir=temp_dir, num_files=32)
device_dir = posixpath.join(self.DEVICE_TEMP_DIR, 'sync_src_dst')
# Clean up any stale files on the device.
device = adb.get_device() # pylint: disable=no-member
device.shell(['rm', '-rf', device_dir])
device.push(temp_dir, device_dir, sync=True)
self.verify_sync(device, temp_files, device_dir)
self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
finally:
if temp_dir is not None:
shutil.rmtree(temp_dir)
def test_unicode_paths(self):
"""Ensure that we can support non-ASCII paths, even on Windows."""
name = u'로보카 폴리'