Merge "adb: allow multiple args to push/pull."

This commit is contained in:
Josh Gao 2015-11-03 02:22:08 +00:00 committed by Gerrit Code Review
commit fe54c96ab2
3 changed files with 152 additions and 81 deletions

View file

@ -33,6 +33,7 @@
#include <memory> #include <memory>
#include <string> #include <string>
#include <vector>
#include <base/logging.h> #include <base/logging.h>
#include <base/stringprintf.h> #include <base/stringprintf.h>
@ -101,11 +102,11 @@ static void help() {
" will disconnect from all connected TCP/IP devices.\n" " will disconnect from all connected TCP/IP devices.\n"
"\n" "\n"
"device commands:\n" "device commands:\n"
" adb push <local> <remote>\n" " adb push <local>... <remote>\n"
" - copy file/dir to device\n" " - copy files/dirs to device\n"
" adb pull [-a] <remote> [<local>]\n" " adb pull [-a] <remote>... <local>\n"
" - copy file/dir from device\n" " - copy files/dirs from device\n"
" ('-a' means copy timestamp and mode)\n" " (-a preserves file timestamp and mode)\n"
" adb sync [ <directory> ] - copy host->device only if changed\n" " adb sync [ <directory> ] - copy host->device only if changed\n"
" (-l means list but don't copy)\n" " (-l means list but don't copy)\n"
" adb shell [-Ttx] - run remote shell interactively\n" " adb shell [-Ttx] - run remote shell interactively\n"
@ -1065,31 +1066,35 @@ static std::string find_product_out_path(const std::string& hint) {
return path; return path;
} }
static void parse_push_pull_args(const char **arg, int narg, static void parse_push_pull_args(const char** arg, int narg,
char const **path1, char const **path2, std::vector<const char*>* srcs,
int *copy_attrs) { const char** dst, bool* copy_attrs) {
*copy_attrs = 0; *copy_attrs = false;
srcs->clear();
bool ignore_flags = false;
while (narg > 0) { while (narg > 0) {
if (!strcmp(*arg, "-p")) { if (ignore_flags || *arg[0] != '-') {
// Silently ignore for backwards compatibility. srcs->push_back(*arg);
} else if (!strcmp(*arg, "-a")) {
*copy_attrs = 1;
} else { } else {
break; if (!strcmp(*arg, "-p")) {
// Silently ignore for backwards compatibility.
} else if (!strcmp(*arg, "-a")) {
*copy_attrs = true;
} else if (!strcmp(*arg, "--")) {
ignore_flags = true;
} else {
fprintf(stderr, "adb: unrecognized option '%s'\n", *arg);
exit(1);
}
} }
++arg; ++arg;
--narg; --narg;
} }
if (narg > 0) { if (srcs->size() > 1) {
*path1 = *arg; *dst = srcs->back();
++arg; srcs->pop_back();
--narg;
}
if (narg > 0) {
*path2 = *arg;
} }
} }
@ -1561,20 +1566,22 @@ int adb_commandline(int argc, const char **argv) {
return do_sync_ls(argv[1]) ? 0 : 1; return do_sync_ls(argv[1]) ? 0 : 1;
} }
else if (!strcmp(argv[0], "push")) { else if (!strcmp(argv[0], "push")) {
int copy_attrs = 0; bool copy_attrs = false;
const char* lpath = NULL, *rpath = NULL; std::vector<const char*> srcs;
const char* dst = nullptr;
parse_push_pull_args(&argv[1], argc - 1, &lpath, &rpath, &copy_attrs); parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, &copy_attrs);
if (!lpath || !rpath || copy_attrs != 0) return usage(); if (srcs.empty() || !dst) return usage();
return do_sync_push(lpath, rpath) ? 0 : 1; return do_sync_push(srcs, dst) ? 0 : 1;
} }
else if (!strcmp(argv[0], "pull")) { else if (!strcmp(argv[0], "pull")) {
int copy_attrs = 0; bool copy_attrs = false;
const char* rpath = NULL, *lpath = "."; std::vector<const char*> srcs;
const char* dst = ".";
parse_push_pull_args(&argv[1], argc - 1, &rpath, &lpath, &copy_attrs); parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, &copy_attrs);
if (!rpath) return usage(); if (srcs.empty()) return usage();
return do_sync_pull(rpath, lpath, copy_attrs) ? 0 : 1; return do_sync_pull(srcs, dst, copy_attrs) ? 0 : 1;
} }
else if (!strcmp(argv[0], "install")) { else if (!strcmp(argv[0], "install")) {
if (argc < 2) return usage(); if (argc < 2) return usage();
@ -1764,8 +1771,9 @@ static int install_app(TransportType transport, const char* serial, int argc, co
} }
int result = -1; int result = -1;
const char* apk_file = argv[last_apk]; std::vector<const char*> apk_file = {argv[last_apk]};
std::string apk_dest = android::base::StringPrintf(where, adb_basename(apk_file).c_str()); std::string apk_dest = android::base::StringPrintf(
where, adb_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())) 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);

View file

@ -625,32 +625,55 @@ static bool copy_local_dir_remote(SyncConnection& sc, const char* lpath, const c
return true; return true;
} }
bool do_sync_push(const char* lpath, const char* rpath) { bool do_sync_push(const std::vector<const char*>& srcs, const char* dst) {
SyncConnection sc; SyncConnection sc;
if (!sc.IsValid()) return false; if (!sc.IsValid()) return false;
struct stat st; bool success = true;
if (stat(lpath, &st)) {
sc.Error("cannot stat '%s': %s", lpath, strerror(errno));
return false;
}
if (S_ISDIR(st.st_mode)) {
return copy_local_dir_remote(sc, lpath, rpath, false, false);
}
unsigned mode; unsigned mode;
if (!sync_stat(sc, rpath, nullptr, &mode, nullptr)) return false; if (!sync_stat(sc, dst, nullptr, &mode, nullptr)) return false;
std::string path_holder; bool dst_isdir = mode != 0 && S_ISDIR(mode);
if (mode != 0 && S_ISDIR(mode)) {
// If we're copying a local file to a remote directory, if (!dst_isdir) {
// we really want to copy to remote_dir + "/" + local_filename. if (srcs.size() > 1) {
path_holder = android::base::StringPrintf("%s/%s", rpath, adb_basename(lpath).c_str()); sc.Error("target '%s' is not a directory", dst);
rpath = path_holder.c_str(); return false;
} else {
size_t dst_len = strlen(dst);
if (dst[dst_len - 1] == '/') {
sc.Error("failed to access '%s': Not a directory", dst);
return false;
}
}
} }
bool result = sync_send(sc, lpath, rpath, st.st_mtime, st.st_mode);
for (const char* src_path : srcs) {
const char* dst_path = dst;
struct stat st;
if (stat(src_path, &st)) {
sc.Error("cannot stat '%s': %s", src_path, strerror(errno));
success = false;
continue;
}
if (S_ISDIR(st.st_mode)) {
success &= copy_local_dir_remote(sc, src_path, dst, false, false);
continue;
}
std::string path_holder;
if (mode != 0 && S_ISDIR(mode)) {
// If we're copying a local file to a remote directory,
// we really want to copy to remote_dir + "/" + local_filename.
path_holder = android::base::StringPrintf(
"%s/%s", dst_path, adb_basename(src_path).c_str());
dst_path = path_holder.c_str();
}
success &= sync_send(sc, src_path, dst_path, st.st_mtime, st.st_mode);
}
sc.Print("\n"); sc.Print("\n");
return result; return success;
} }
struct sync_ls_build_list_cb_args { struct sync_ls_build_list_cb_args {
@ -733,7 +756,7 @@ static int set_time_and_mode(const char *lpath, time_t time, unsigned int mode)
} }
static bool copy_remote_dir_local(SyncConnection& sc, const char* rpath, const char* lpath, static bool copy_remote_dir_local(SyncConnection& sc, const char* rpath, const char* lpath,
int copy_attrs) { bool copy_attrs) {
// Make sure that both directory paths end in a slash. // Make sure that both directory paths end in a slash.
std::string rpath_clean(rpath); std::string rpath_clean(rpath);
std::string lpath_clean(lpath); std::string lpath_clean(lpath);
@ -774,43 +797,80 @@ static bool copy_remote_dir_local(SyncConnection& sc, const char* rpath, const c
return true; return true;
} }
bool do_sync_pull(const char* rpath, const char* lpath, int copy_attrs) { bool do_sync_pull(const std::vector<const char*>& srcs, const char* dst,
bool copy_attrs) {
SyncConnection sc; SyncConnection sc;
if (!sc.IsValid()) return false; if (!sc.IsValid()) return false;
bool success = true;
unsigned mode, time; unsigned mode, time;
if (!sync_stat(sc, rpath, &time, &mode, nullptr)) return false; struct stat st;
if (mode == 0) { if (stat(dst, &st)) {
sc.Error("remote object '%s' does not exist", rpath); // If we're only pulling one file, the destination path might point to
return false; // a path that doesn't exist yet.
if (srcs.size() != 1 || errno != ENOENT) {
sc.Error("cannot stat '%s': %s", dst, strerror(errno));
return false;
}
} }
if (S_ISREG(mode) || S_ISLNK(mode) || S_ISCHR(mode) || S_ISBLK(mode)) { bool dst_isdir = S_ISDIR(st.st_mode);
std::string path_holder; if (!dst_isdir) {
struct stat st; if (srcs.size() > 1) {
if (stat(lpath, &st) == 0) { sc.Error("target '%s' is not a directory", dst);
if (S_ISDIR(st.st_mode)) {
// If we're copying a remote file to a local directory,
// we really want to copy to local_dir + "/" + basename(remote).
path_holder = android::base::StringPrintf("%s/%s", lpath, adb_basename(rpath).c_str());
lpath = path_holder.c_str();
}
}
if (!sync_recv(sc, rpath, lpath)) {
return false; return false;
} else { } else {
if (copy_attrs && set_time_and_mode(lpath, time, mode)) { size_t dst_len = strlen(dst);
if (dst[dst_len - 1] == '/') {
sc.Error("failed to access '%s': Not a directory", dst);
return false; return false;
} }
} }
sc.Print("\n");
return true;
} else if (S_ISDIR(mode)) {
return copy_remote_dir_local(sc, rpath, lpath, copy_attrs);
} }
sc.Error("remote object '%s' not a file or directory", rpath); for (const char* src_path : srcs) {
return false; const char* dst_path = dst;
if (!sync_stat(sc, src_path, &time, &mode, nullptr)) return false;
if (mode == 0) {
sc.Error("remote object '%s' does not exist", src_path);
success = false;
continue;
}
if (S_ISREG(mode) || S_ISLNK(mode) || S_ISCHR(mode) || S_ISBLK(mode)) {
std::string path_holder;
struct stat st;
if (stat(dst_path, &st) == 0) {
if (S_ISDIR(st.st_mode)) {
// If we're copying a remote file to a local directory,
// we really want to copy to local_dir + "/" +
// basename(remote).
path_holder = android::base::StringPrintf(
"%s/%s", dst_path, adb_basename(src_path).c_str());
dst_path = path_holder.c_str();
}
}
if (!sync_recv(sc, src_path, dst_path)) {
success = false;
continue;
} else {
if (copy_attrs && set_time_and_mode(dst_path, time, mode)) {
success = false;
continue;
}
}
} else if (S_ISDIR(mode)) {
success &= copy_remote_dir_local(sc, src_path, dst_path, copy_attrs);
continue;
} else {
sc.Error("remote object '%s' not a file or directory", src_path);
success = false;
continue;
}
}
sc.Print("\n");
return success;
} }
bool do_sync_sync(const std::string& lpath, const std::string& rpath, bool list_only) { bool do_sync_sync(const std::string& lpath, const std::string& rpath, bool list_only) {

View file

@ -18,6 +18,7 @@
#define _FILE_SYNC_SERVICE_H_ #define _FILE_SYNC_SERVICE_H_
#include <string> #include <string>
#include <vector>
#define MKID(a,b,c,d) ((a) | ((b) << 8) | ((c) << 16) | ((d) << 24)) #define MKID(a,b,c,d) ((a) | ((b) << 8) | ((c) << 16) | ((d) << 24))
@ -64,9 +65,11 @@ 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 char* lpath, const char* rpath); bool do_sync_push(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);
bool do_sync_sync(const std::string& lpath, const std::string& rpath, bool list_only); bool do_sync_sync(const std::string& lpath, const std::string& rpath, bool list_only);
bool do_sync_pull(const char* rpath, const char* lpath, int copy_attrs);
#define SYNC_DATA_MAX (64*1024) #define SYNC_DATA_MAX (64*1024)