Merge "Add --sync support to push."
am: 62db5fcee0
Change-Id: I29d2bd53c13a4b92280b617666cd258acd5c2610
This commit is contained in:
commit
d50b10544b
4 changed files with 70 additions and 27 deletions
|
|
@ -126,8 +126,9 @@ static void help() {
|
||||||
" reverse --remove-all remove all reverse socket connections from device\n"
|
" reverse --remove-all remove all reverse socket connections from device\n"
|
||||||
"\n"
|
"\n"
|
||||||
"file transfer:\n"
|
"file transfer:\n"
|
||||||
" push LOCAL... REMOTE\n"
|
" push [--sync] LOCAL... REMOTE\n"
|
||||||
" copy local files/directories to device\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"
|
" pull [-a] REMOTE... LOCAL\n"
|
||||||
" copy files/dirs from device\n"
|
" copy files/dirs from device\n"
|
||||||
" -a: preserve file timestamp and mode\n"
|
" -a: preserve file timestamp and mode\n"
|
||||||
|
|
@ -1233,9 +1234,8 @@ static int restore(int argc, const char** argv) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void parse_push_pull_args(const char** arg, int narg,
|
static void parse_push_pull_args(const char** arg, int narg, std::vector<const char*>* srcs,
|
||||||
std::vector<const char*>* srcs,
|
const char** dst, bool* copy_attrs, bool* sync) {
|
||||||
const char** dst, bool* copy_attrs) {
|
|
||||||
*copy_attrs = false;
|
*copy_attrs = false;
|
||||||
|
|
||||||
srcs->clear();
|
srcs->clear();
|
||||||
|
|
@ -1248,6 +1248,10 @@ static void parse_push_pull_args(const char** arg, int narg,
|
||||||
// Silently ignore for backwards compatibility.
|
// Silently ignore for backwards compatibility.
|
||||||
} else if (!strcmp(*arg, "-a")) {
|
} else if (!strcmp(*arg, "-a")) {
|
||||||
*copy_attrs = true;
|
*copy_attrs = true;
|
||||||
|
} else if (!strcmp(*arg, "--sync")) {
|
||||||
|
if (sync != nullptr) {
|
||||||
|
*sync = true;
|
||||||
|
}
|
||||||
} else if (!strcmp(*arg, "--")) {
|
} else if (!strcmp(*arg, "--")) {
|
||||||
ignore_flags = true;
|
ignore_flags = true;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -1654,19 +1658,20 @@ int adb_commandline(int argc, const char** argv) {
|
||||||
}
|
}
|
||||||
else if (!strcmp(argv[0], "push")) {
|
else if (!strcmp(argv[0], "push")) {
|
||||||
bool copy_attrs = false;
|
bool copy_attrs = false;
|
||||||
|
bool sync = false;
|
||||||
std::vector<const char*> srcs;
|
std::vector<const char*> srcs;
|
||||||
const char* dst = nullptr;
|
const char* dst = nullptr;
|
||||||
|
|
||||||
parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, ©_attrs);
|
parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, ©_attrs, &sync);
|
||||||
if (srcs.empty() || !dst) return syntax_error("push requires an argument");
|
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")) {
|
else if (!strcmp(argv[0], "pull")) {
|
||||||
bool copy_attrs = false;
|
bool copy_attrs = false;
|
||||||
std::vector<const char*> srcs;
|
std::vector<const char*> srcs;
|
||||||
const char* dst = ".";
|
const char* dst = ".";
|
||||||
|
|
||||||
parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, ©_attrs);
|
parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, ©_attrs, nullptr);
|
||||||
if (srcs.empty()) return syntax_error("pull requires an argument");
|
if (srcs.empty()) return syntax_error("pull requires an argument");
|
||||||
return do_sync_pull(srcs, dst, copy_attrs) ? 0 : 1;
|
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::vector<const char*> apk_file = {argv[last_apk]};
|
||||||
std::string apk_dest = android::base::StringPrintf(
|
std::string apk_dest = android::base::StringPrintf(
|
||||||
where, android::base::Basename(argv[last_apk]).c_str());
|
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 */
|
argv[last_apk] = apk_dest.c_str(); /* destination name, not source location */
|
||||||
result = pm_command(transport, serial, argc, argv);
|
result = pm_command(transport, serial, argc, argv);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -674,11 +674,22 @@ static bool sync_stat_fallback(SyncConnection& sc, const char* path, struct stat
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool sync_send(SyncConnection& sc, const char* lpath, const char* rpath,
|
static bool sync_send(SyncConnection& sc, const char* lpath, const char* rpath, unsigned mtime,
|
||||||
unsigned mtime, mode_t mode)
|
mode_t mode, bool sync) {
|
||||||
{
|
|
||||||
std::string path_and_mode = android::base::StringPrintf("%s,%d", rpath, mode);
|
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 (S_ISLNK(mode)) {
|
||||||
#if !defined(_WIN32)
|
#if !defined(_WIN32)
|
||||||
char buf[PATH_MAX];
|
char buf[PATH_MAX];
|
||||||
|
|
@ -902,7 +913,7 @@ static bool copy_local_dir_remote(SyncConnection& sc, std::string lpath,
|
||||||
if (list_only) {
|
if (list_only) {
|
||||||
sc.Println("would push: %s -> %s", ci.lpath.c_str(), ci.rpath.c_str());
|
sc.Println("would push: %s -> %s", ci.lpath.c_str(), ci.rpath.c_str());
|
||||||
} else {
|
} 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;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -916,7 +927,7 @@ static bool copy_local_dir_remote(SyncConnection& sc, std::string lpath,
|
||||||
return true;
|
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;
|
SyncConnection sc;
|
||||||
if (!sc.IsValid()) return false;
|
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));
|
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;
|
continue;
|
||||||
} else if (!should_push_file(st.st_mode)) {
|
} else if (!should_push_file(st.st_mode)) {
|
||||||
sc.Warning("skipping special file '%s' (mode = 0o%o)", src_path, 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.NewTransfer();
|
||||||
sc.SetExpectedTotalBytes(st.st_size);
|
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);
|
sc.ReportTransferRate(src_path, TransferDirection::push);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,7 @@ union syncmsg {
|
||||||
|
|
||||||
void file_sync_service(int fd, void* cookie);
|
void file_sync_service(int fd, void* cookie);
|
||||||
bool do_sync_ls(const char* path);
|
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 do_sync_pull(const std::vector<const char*>& srcs, const char* dst,
|
||||||
bool copy_attrs, const char* name=nullptr);
|
bool copy_attrs, const char* name=nullptr);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1130,8 +1130,18 @@ class FileOperationsTest(DeviceTest):
|
||||||
if host_dir is not None:
|
if host_dir is not None:
|
||||||
shutil.rmtree(host_dir)
|
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):
|
def test_sync(self):
|
||||||
"""Sync a randomly generated directory of files to specified device."""
|
"""Sync a host directory to the data partition."""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
base_dir = tempfile.mkdtemp()
|
base_dir = tempfile.mkdtemp()
|
||||||
|
|
@ -1141,9 +1151,10 @@ class FileOperationsTest(DeviceTest):
|
||||||
os.makedirs(full_dir_path)
|
os.makedirs(full_dir_path)
|
||||||
|
|
||||||
# Create 32 random files within the host mirror.
|
# 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 = adb.get_device() # pylint: disable=no-member
|
||||||
device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
|
device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
|
||||||
|
|
||||||
|
|
@ -1155,19 +1166,35 @@ class FileOperationsTest(DeviceTest):
|
||||||
else:
|
else:
|
||||||
os.environ['ANDROID_PRODUCT_OUT'] = old_product_out
|
os.environ['ANDROID_PRODUCT_OUT'] = old_product_out
|
||||||
|
|
||||||
# Confirm that every file on the device mirrors that on the host.
|
self.verify_sync(device, temp_files, self.DEVICE_TEMP_DIR)
|
||||||
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.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
|
#self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
|
||||||
finally:
|
finally:
|
||||||
if base_dir is not None:
|
if base_dir is not None:
|
||||||
shutil.rmtree(base_dir)
|
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):
|
def test_unicode_paths(self):
|
||||||
"""Ensure that we can support non-ASCII paths, even on Windows."""
|
"""Ensure that we can support non-ASCII paths, even on Windows."""
|
||||||
name = u'로보카 폴리'
|
name = u'로보카 폴리'
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue