diff options
| -rw-r--r-- | adb/Android.mk | 5 | ||||
| -rw-r--r-- | adb/commandline.cpp | 25 | ||||
| -rw-r--r-- | adb/file_sync_client.cpp | 263 | ||||
| -rw-r--r-- | adb/file_sync_service.h | 4 | ||||
| -rw-r--r-- | adb/line_printer.cpp | 161 | ||||
| -rw-r--r-- | adb/line_printer.h | 71 |
6 files changed, 387 insertions, 142 deletions
diff --git a/adb/Android.mk b/adb/Android.mk index c507905dbe..903d1e15a1 100644 --- a/adb/Android.mk +++ b/adb/Android.mk @@ -213,12 +213,13 @@ LOCAL_STATIC_LIBRARIES_windows := AdbWinApi LOCAL_REQUIRED_MODULES_windows := AdbWinApi AdbWinUsbApi LOCAL_SRC_FILES := \ + adb_client.cpp \ client/main.cpp \ console.cpp \ commandline.cpp \ - adb_client.cpp \ - services.cpp \ file_sync_client.cpp \ + line_printer.cpp \ + services.cpp \ shell_service_protocol.cpp \ LOCAL_CFLAGS += \ diff --git a/adb/commandline.cpp b/adb/commandline.cpp index 2632b1f31b..a7208593fe 100644 --- a/adb/commandline.cpp +++ b/adb/commandline.cpp @@ -101,12 +101,10 @@ static void help() { " will disconnect from all connected TCP/IP devices.\n" "\n" "device commands:\n" - " adb push [-p] <local> <remote>\n" + " adb push <local> <remote>\n" " - copy file/dir to device\n" - " ('-p' to display the transfer progress)\n" - " adb pull [-p] [-a] <remote> [<local>]\n" + " adb pull [-a] <remote> [<local>]\n" " - copy file/dir from device\n" - " ('-p' to display the transfer progress)\n" " ('-a' means copy timestamp and mode)\n" " adb sync [ <directory> ] - copy host->device only if changed\n" " (-l means list but don't copy)\n" @@ -1065,15 +1063,14 @@ static std::string find_product_out_path(const std::string& hint) { return path; } -static void parse_push_pull_args(const char **arg, int narg, char const **path1, - char const **path2, bool* show_progress, +static void parse_push_pull_args(const char **arg, int narg, + char const **path1, char const **path2, int *copy_attrs) { - *show_progress = false; *copy_attrs = 0; while (narg > 0) { if (!strcmp(*arg, "-p")) { - *show_progress = true; + // Silently ignore for backwards compatibility. } else if (!strcmp(*arg, "-a")) { *copy_attrs = 1; } else { @@ -1561,22 +1558,20 @@ int adb_commandline(int argc, const char **argv) { return do_sync_ls(argv[1]) ? 0 : 1; } else if (!strcmp(argv[0], "push")) { - bool show_progress = false; int copy_attrs = 0; const char* lpath = NULL, *rpath = NULL; - parse_push_pull_args(&argv[1], argc - 1, &lpath, &rpath, &show_progress, ©_attrs); + parse_push_pull_args(&argv[1], argc - 1, &lpath, &rpath, ©_attrs); if (!lpath || !rpath || copy_attrs != 0) return usage(); - return do_sync_push(lpath, rpath, show_progress) ? 0 : 1; + return do_sync_push(lpath, rpath) ? 0 : 1; } else if (!strcmp(argv[0], "pull")) { - bool show_progress = false; int copy_attrs = 0; const char* rpath = NULL, *lpath = "."; - parse_push_pull_args(&argv[1], argc - 1, &rpath, &lpath, &show_progress, ©_attrs); + parse_push_pull_args(&argv[1], argc - 1, &rpath, &lpath, ©_attrs); if (!rpath) return usage(); - return do_sync_pull(rpath, lpath, show_progress, copy_attrs) ? 0 : 1; + return do_sync_pull(rpath, lpath, copy_attrs) ? 0 : 1; } else if (!strcmp(argv[0], "install")) { if (argc < 2) return usage(); @@ -1768,7 +1763,7 @@ static int install_app(TransportType transport, const char* serial, int argc, co int result = -1; const char* apk_file = argv[last_apk]; std::string apk_dest = android::base::StringPrintf(where, adb_basename(apk_file).c_str()); - if (!do_sync_push(apk_file, apk_dest.c_str(), false)) 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 */ result = pm_command(transport, serial, argc, argv); diff --git a/adb/file_sync_client.cpp b/adb/file_sync_client.cpp index 1d668bc180..b7e835ba29 100644 --- a/adb/file_sync_client.cpp +++ b/adb/file_sync_client.cpp @@ -38,6 +38,7 @@ #include "adb_io.h" #include "adb_utils.h" #include "file_sync_service.h" +#include "line_printer.h" #include <base/file.h> #include <base/strings.h> @@ -49,36 +50,15 @@ struct syncsendbuf { char data[SYNC_DATA_MAX]; }; -static long long NOW() { - struct timeval tv; - gettimeofday(&tv, 0); - return ((long long) tv.tv_usec) + 1000000LL * ((long long) tv.tv_sec); -} - -static void print_transfer_progress(uint64_t bytes_current, - uint64_t bytes_total) { - if (bytes_total == 0) return; - - fprintf(stderr, "\rTransferring: %" PRIu64 "/%" PRIu64 " (%d%%)", - bytes_current, bytes_total, - (int) (bytes_current * 100 / bytes_total)); - - if (bytes_current == bytes_total) { - fputc('\n', stderr); - } - - fflush(stderr); -} - class SyncConnection { public: - SyncConnection() : total_bytes(0), start_time_(NOW()) { + SyncConnection() : total_bytes(0), start_time_ms_(CurrentTimeMs()) { max = SYNC_DATA_MAX; // TODO: decide at runtime. std::string error; fd = adb_connect("sync:", &error); if (fd < 0) { - fprintf(stderr, "adb: error: %s\n", error.c_str()); + Error("connect failed: %s", error.c_str()); } } @@ -86,7 +66,6 @@ class SyncConnection { if (!IsValid()) return; SendQuit(); - ShowTransferRate(); adb_close(fd); } @@ -95,7 +74,7 @@ class SyncConnection { bool SendRequest(int id, const char* path_and_mode) { size_t path_length = strlen(path_and_mode); if (path_length > 1024) { - fprintf(stderr, "adb: SendRequest failed: path too long: %zu\n", path_length); + Error("SendRequest failed: path too long: %zu", path_length); errno = ENAMETOOLONG; return false; } @@ -115,11 +94,14 @@ class SyncConnection { // Sending header, payload, and footer in a single write makes a huge // difference to "adb sync" performance. bool SendSmallFile(const char* path_and_mode, + const char* rpath, const char* data, size_t data_length, unsigned mtime) { + Print(rpath); + size_t path_length = strlen(path_and_mode); if (path_length > 1024) { - fprintf(stderr, "adb: SendSmallFile failed: path too long: %zu\n", path_length); + Error("SendSmallFile failed: path too long: %zu", path_length); errno = ENAMETOOLONG; return false; } @@ -157,16 +139,14 @@ class SyncConnection { bool CopyDone(const char* from, const char* to) { syncmsg msg; if (!ReadFdExactly(fd, &msg.status, sizeof(msg.status))) { - fprintf(stderr, "adb: failed to copy '%s' to '%s': no ID_DONE: %s\n", - from, to, strerror(errno)); + Error("failed to copy '%s' to '%s': no ID_DONE: %s", from, to, strerror(errno)); return false; } if (msg.status.id == ID_OKAY) { return true; } if (msg.status.id != ID_FAIL) { - fprintf(stderr, "adb: failed to copy '%s' to '%s': unknown reason %d\n", - from, to, msg.status.id); + Error("failed to copy '%s' to '%s': unknown reason %d", from, to, msg.status.id); return false; } return ReportCopyFailure(from, to, msg); @@ -175,15 +155,41 @@ class SyncConnection { bool ReportCopyFailure(const char* from, const char* to, const syncmsg& msg) { std::vector<char> buf(msg.status.msglen + 1); if (!ReadFdExactly(fd, &buf[0], msg.status.msglen)) { - fprintf(stderr, "adb: failed to copy '%s' to '%s'; failed to read reason (!): %s\n", - from, to, strerror(errno)); + Error("failed to copy '%s' to '%s'; failed to read reason (!): %s", + from, to, strerror(errno)); return false; } buf[msg.status.msglen] = 0; - fprintf(stderr, "adb: failed to copy '%s' to '%s': %s\n", from, to, &buf[0]); + Error("failed to copy '%s' to '%s': %s", from, to, &buf[0]); return false; } + std::string TransferRate() { + uint64_t ms = CurrentTimeMs() - start_time_ms_; + if (total_bytes == 0 || ms == 0) return ""; + + double s = static_cast<double>(ms) / 1000LL; + double rate = (static_cast<double>(total_bytes) / s) / (1024*1024); + return android::base::StringPrintf(" %.1f MB/s (%" PRId64 " bytes in %.3fs)", + rate, total_bytes, s); + } + + void Print(const std::string& s) { + // TODO: we actually don't want ELIDE; we want "ELIDE if smart, FULL if dumb". + line_printer_.Print(s, LinePrinter::ELIDE); + } + + void Error(const char* fmt, ...) __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 3))) { + std::string s = "adb: error: "; + + va_list ap; + va_start(ap, fmt); + android::base::StringAppendV(&s, fmt, ap); + va_end(ap); + + line_printer_.Print(s, LinePrinter::FULL); + } + uint64_t total_bytes; // TODO: add a char[max] buffer here, to replace syncsendbuf... @@ -191,19 +197,18 @@ class SyncConnection { size_t max; private: - uint64_t start_time_; + uint64_t start_time_ms_; + + LinePrinter line_printer_; void SendQuit() { SendRequest(ID_QUIT, ""); // TODO: add a SendResponse? } - void ShowTransferRate() { - uint64_t t = NOW() - start_time_; - if (total_bytes == 0 || t == 0) return; - - fprintf(stderr, "%lld KB/s (%" PRId64 " bytes in %lld.%03llds)\n", - ((total_bytes * 1000000LL) / t) / 1024LL, - total_bytes, (t / 1000000LL), (t % 1000000LL) / 1000LL); + static uint64_t CurrentTimeMs() { + struct timeval tv; + gettimeofday(&tv, 0); // (Not clock_gettime because of Mac/Windows.) + return static_cast<uint64_t>(tv.tv_sec) * 1000 + tv.tv_usec / 1000; } }; @@ -249,29 +254,26 @@ static bool sync_stat(SyncConnection& sc, const char* path, return sc.SendRequest(ID_STAT, path) && sync_finish_stat(sc, timestamp, mode, size); } -static bool SendLargeFile(SyncConnection& sc, const char* path_and_mode, const char* path, - unsigned mtime, bool show_progress) { +static bool SendLargeFile(SyncConnection& sc, const char* path_and_mode, + const char* lpath, const char* rpath, + unsigned mtime) { if (!sc.SendRequest(ID_SEND, path_and_mode)) { - fprintf(stderr, "adb: failed to send ID_SEND message '%s': %s\n", - path_and_mode, strerror(errno)); + sc.Error("failed to send ID_SEND message '%s': %s", path_and_mode, strerror(errno)); return false; } - unsigned long long size = 0; - if (show_progress) { - // Determine local file size. - struct stat st; - if (stat(path, &st) == -1) { - fprintf(stderr, "adb: cannot stat '%s': %s\n", path, strerror(errno)); - return false; - } - - size = st.st_size; + struct stat st; + if (stat(lpath, &st) == -1) { + sc.Error("cannot stat '%s': %s", lpath, strerror(errno)); + return false; } - int lfd = adb_open(path, O_RDONLY); + uint64_t total_size = st.st_size; + uint64_t bytes_copied = 0; + + int lfd = adb_open(lpath, O_RDONLY); if (lfd < 0) { - fprintf(stderr, "adb: cannot open '%s': %s\n", path, strerror(errno)); + sc.Error("cannot open '%s': %s", lpath, strerror(errno)); return false; } @@ -281,7 +283,7 @@ static bool SendLargeFile(SyncConnection& sc, const char* path_and_mode, const c int ret = adb_read(lfd, sbuf.data, sc.max); if (ret <= 0) { if (ret < 0) { - fprintf(stderr, "adb: cannot read '%s': %s\n", path, strerror(errno)); + sc.Error("cannot read '%s': %s", lpath, strerror(errno)); adb_close(lfd); return false; } @@ -295,9 +297,10 @@ static bool SendLargeFile(SyncConnection& sc, const char* path_and_mode, const c } sc.total_bytes += ret; - if (show_progress) { - print_transfer_progress(sc.total_bytes, size); - } + bytes_copied += ret; + + int percentage = static_cast<int>(bytes_copied * 100 / total_size); + sc.Print(android::base::StringPrintf("%s: %d%%", rpath, percentage)); } adb_close(lfd); @@ -306,8 +309,7 @@ static bool SendLargeFile(SyncConnection& sc, const char* path_and_mode, const c msg.data.id = ID_DONE; msg.data.size = mtime; if (!WriteFdExactly(sc.fd, &msg.data, sizeof(msg.data))) { - fprintf(stderr, "adb: failed to send ID_DONE message for '%s': %s\n", - path, strerror(errno)); + sc.Error("failed to send ID_DONE message for '%s': %s", rpath, strerror(errno)); return false; } @@ -315,7 +317,7 @@ static bool SendLargeFile(SyncConnection& sc, const char* path_and_mode, const c } static bool sync_send(SyncConnection& sc, const char* lpath, const char* rpath, - unsigned mtime, mode_t mode, bool show_progress) + unsigned mtime, mode_t mode) { std::string path_and_mode = android::base::StringPrintf("%s,%d", rpath, mode); @@ -324,44 +326,48 @@ static bool sync_send(SyncConnection& sc, const char* lpath, const char* rpath, char buf[PATH_MAX]; ssize_t data_length = readlink(lpath, buf, PATH_MAX - 1); if (data_length == -1) { - fprintf(stderr, "adb: readlink '%s' failed: %s\n", lpath, strerror(errno)); + sc.Error("readlink '%s' failed: %s", lpath, strerror(errno)); return false; } buf[data_length++] = '\0'; - if (!sc.SendSmallFile(path_and_mode.c_str(), buf, data_length, mtime)) return false; + if (!sc.SendSmallFile(path_and_mode.c_str(), rpath, buf, data_length, mtime)) return false; return sc.CopyDone(lpath, rpath); #endif } if (!S_ISREG(mode)) { - fprintf(stderr, "adb: local file '%s' has unsupported mode: 0o%o\n", lpath, mode); + sc.Error("local file '%s' has unsupported mode: 0o%o", lpath, mode); return false; } struct stat st; if (stat(lpath, &st) == -1) { - fprintf(stderr, "adb: failed to stat local file '%s': %s\n", lpath, strerror(errno)); + sc.Error("failed to stat local file '%s': %s", lpath, strerror(errno)); return false; } if (st.st_size < SYNC_DATA_MAX) { std::string data; if (!android::base::ReadFileToString(lpath, &data)) { - fprintf(stderr, "adb: failed to read all of '%s': %s\n", lpath, strerror(errno)); + sc.Error("failed to read all of '%s': %s", lpath, strerror(errno)); + return false; + } + if (!sc.SendSmallFile(path_and_mode.c_str(), rpath, data.data(), data.size(), mtime)) { return false; } - if (!sc.SendSmallFile(path_and_mode.c_str(), data.data(), data.size(), mtime)) return false; } else { - if (!SendLargeFile(sc, path_and_mode.c_str(), lpath, mtime, show_progress)) return false; + if (!SendLargeFile(sc, path_and_mode.c_str(), lpath, rpath, mtime)) { + return false; + } } return sc.CopyDone(lpath, rpath); } -static bool sync_recv(SyncConnection& sc, const char* rpath, const char* lpath, bool show_progress) { +static bool sync_recv(SyncConnection& sc, const char* rpath, const char* lpath) { + sc.Print(rpath); + unsigned size = 0; - if (show_progress) { - if (!sync_stat(sc, rpath, nullptr, nullptr, &size)) return false; - } + if (!sync_stat(sc, rpath, nullptr, nullptr, &size)) return false; if (!sc.SendRequest(ID_RECV, rpath)) return false; @@ -369,10 +375,11 @@ static bool sync_recv(SyncConnection& sc, const char* rpath, const char* lpath, mkdirs(lpath); int lfd = adb_creat(lpath, 0644); if (lfd < 0) { - fprintf(stderr, "adb: cannot create '%s': %s\n", lpath, strerror(errno)); + sc.Error("cannot create '%s': %s", lpath, strerror(errno)); return false; } + uint64_t bytes_copied = 0; while (true) { syncmsg msg; if (!ReadFdExactly(sc.fd, &msg.data, sizeof(msg.data))) { @@ -391,7 +398,7 @@ static bool sync_recv(SyncConnection& sc, const char* rpath, const char* lpath, } if (msg.data.size > sc.max) { - fprintf(stderr, "adb: msg.data.size too large: %u (max %zu)\n", msg.data.size, sc.max); + sc.Error("msg.data.size too large: %u (max %zu)", msg.data.size, sc.max); adb_close(lfd); adb_unlink(lpath); return false; @@ -405,7 +412,7 @@ static bool sync_recv(SyncConnection& sc, const char* rpath, const char* lpath, } if (!WriteFdExactly(lfd, buffer, msg.data.size)) { - fprintf(stderr, "adb: cannot write '%s': %s\n", lpath, strerror(errno)); + sc.Error("cannot write '%s': %s", lpath, strerror(errno)); adb_close(lfd); adb_unlink(lpath); return false; @@ -413,9 +420,10 @@ static bool sync_recv(SyncConnection& sc, const char* rpath, const char* lpath, sc.total_bytes += msg.data.size; - if (show_progress) { - print_transfer_progress(sc.total_bytes, size); - } + bytes_copied += msg.data.size; + + int percentage = static_cast<int>(bytes_copied * 100 / size); + sc.Print(android::base::StringPrintf("%s: %d%%", rpath, percentage)); } adb_close(lfd); @@ -453,7 +461,7 @@ static copyinfo* mkcopyinfo(const char* spath, const char* dpath, const char* na int dsize = dlen + nlen + 2; copyinfo *ci = reinterpret_cast<copyinfo*>(malloc(sizeof(copyinfo) + ssize + dsize)); - if(ci == 0) { + if (ci == 0) { fprintf(stderr, "out of memory\n"); abort(); } @@ -475,23 +483,24 @@ static bool IsDotOrDotDot(const char* name) { return name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')); } -static int local_build_list(copyinfo** filelist, const char* lpath, const char* rpath) { +static int local_build_list(SyncConnection& sc, + copyinfo** filelist, const char* lpath, const char* rpath) { copyinfo *dirlist = 0; copyinfo *ci, *next; std::unique_ptr<DIR, int(*)(DIR*)> dir(opendir(lpath), closedir); if (!dir) { - fprintf(stderr, "adb: cannot open '%s': %s\n", lpath, strerror(errno)); + sc.Error("cannot open '%s': %s", lpath, strerror(errno)); return -1; } - dirent *de; + dirent* de; while ((de = readdir(dir.get()))) { if (IsDotOrDotDot(de->d_name)) continue; char stat_path[PATH_MAX]; if (strlen(lpath) + strlen(de->d_name) + 1 > sizeof(stat_path)) { - fprintf(stderr, "adb: skipping long path '%s%s'\n", lpath, de->d_name); + sc.Error("skipping long path '%s%s'", lpath, de->d_name); continue; } strcpy(stat_path, lpath); @@ -506,7 +515,7 @@ static int local_build_list(copyinfo** filelist, const char* lpath, const char* } else { ci = mkcopyinfo(lpath, rpath, de->d_name, 0); if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) { - fprintf(stderr, "adb: skipping special file '%s'\n", ci->src); + sc.Error("skipping special file '%s'", ci->src); free(ci); } else { ci->time = st.st_mtime; @@ -517,7 +526,7 @@ static int local_build_list(copyinfo** filelist, const char* lpath, const char* } } } else { - fprintf(stderr, "adb: cannot lstat '%s': %s\n",stat_path , strerror(errno)); + sc.Error("cannot lstat '%s': %s",stat_path , strerror(errno)); } } @@ -525,7 +534,7 @@ static int local_build_list(copyinfo** filelist, const char* lpath, const char* dir.reset(); for (ci = dirlist; ci != 0; ci = next) { next = ci->next; - local_build_list(filelist, ci->src, ci->dst); + local_build_list(sc, filelist, ci->src, ci->dst); free(ci); } @@ -555,7 +564,7 @@ static bool copy_local_dir_remote(SyncConnection& sc, const char* lpath, const c rpath = tmp; } - if (local_build_list(&filelist, lpath, rpath)) { + if (local_build_list(sc, &filelist, lpath, rpath)) { return false; } @@ -578,9 +587,12 @@ static bool copy_local_dir_remote(SyncConnection& sc, const char* lpath, const c for (ci = filelist; ci != 0; ci = next) { next = ci->next; if (ci->flag == 0) { - fprintf(stderr, "%spush: %s -> %s\n", list_only ? "would " : "", ci->src, ci->dst); - if (!list_only && !sync_send(sc, ci->src, ci->dst, ci->time, ci->mode, false)) { - return false; + if (list_only) { + fprintf(stderr, "would push: %s -> %s\n", ci->src, ci->dst); + } else { + if (!sync_send(sc, ci->src, ci->dst, ci->time, ci->mode)) { + return false; + } } pushed++; } else { @@ -589,20 +601,21 @@ static bool copy_local_dir_remote(SyncConnection& sc, const char* lpath, const c free(ci); } - fprintf(stderr, "%d file%s pushed. %d file%s skipped.\n", - pushed, (pushed == 1) ? "" : "s", - skipped, (skipped == 1) ? "" : "s"); - + sc.Print(android::base::StringPrintf("%s: %d file%s pushed. %d file%s skipped.%s\n", + rpath, + pushed, (pushed == 1) ? "" : "s", + skipped, (skipped == 1) ? "" : "s", + sc.TransferRate().c_str())); return true; } -bool do_sync_push(const char* lpath, const char* rpath, bool show_progress) { +bool do_sync_push(const char* lpath, const char* rpath) { SyncConnection sc; if (!sc.IsValid()) return false; struct stat st; if (stat(lpath, &st)) { - fprintf(stderr, "adb: cannot stat '%s': %s\n", lpath, strerror(errno)); + sc.Error("cannot stat '%s': %s", lpath, strerror(errno)); return false; } @@ -619,21 +632,23 @@ bool do_sync_push(const char* lpath, const char* rpath, bool show_progress) { path_holder = android::base::StringPrintf("%s/%s", rpath, adb_basename(lpath).c_str()); rpath = path_holder.c_str(); } - return sync_send(sc, lpath, rpath, st.st_mtime, st.st_mode, show_progress); + bool result = sync_send(sc, lpath, rpath, st.st_mtime, st.st_mode); + sc.Print("\n"); + return result; } - struct sync_ls_build_list_cb_args { - copyinfo **filelist; - copyinfo **dirlist; - const char *rpath; - const char *lpath; + SyncConnection* sc; + copyinfo** filelist; + copyinfo** dirlist; + const char* rpath; + const char* lpath; }; static void sync_ls_build_list_cb(unsigned mode, unsigned size, unsigned time, const char* name, void* cookie) { - sync_ls_build_list_cb_args *args = (sync_ls_build_list_cb_args *)cookie; + sync_ls_build_list_cb_args* args = static_cast<sync_ls_build_list_cb_args*>(cookie); copyinfo *ci; if (S_ISDIR(mode)) { @@ -655,28 +670,29 @@ static void sync_ls_build_list_cb(unsigned mode, unsigned size, unsigned time, ci->next = *filelist; *filelist = ci; } else { - fprintf(stderr, "adb: skipping special file '%s'\n", name); + args->sc->Print(android::base::StringPrintf("skipping special file '%s'\n", name)); } } static bool remote_build_list(SyncConnection& sc, copyinfo **filelist, const char *rpath, const char *lpath) { - copyinfo *dirlist = NULL; - sync_ls_build_list_cb_args args; + copyinfo* dirlist = nullptr; + sync_ls_build_list_cb_args args; + args.sc = ≻ args.filelist = filelist; args.dirlist = &dirlist; args.rpath = rpath; args.lpath = lpath; // Put the files/dirs in rpath on the lists. - if (!sync_ls(sc, rpath, sync_ls_build_list_cb, (void *)&args)) { + if (!sync_ls(sc, rpath, sync_ls_build_list_cb, &args)) { return false; } // Recurse into each directory we found. while (dirlist != NULL) { - copyinfo *next = dirlist->next; + copyinfo* next = dirlist->next; if (!remote_build_list(sc, filelist, dirlist->src, dirlist->dst)) { return false; } @@ -710,7 +726,7 @@ static bool copy_remote_dir_local(SyncConnection& sc, const char* rpath, const c if (lpath_clean.back() != '/') lpath_clean.push_back('/'); // Recursively build the list of files to copy. - fprintf(stderr, "pull: building file list...\n"); + sc.Print("pull: building file list..."); copyinfo* filelist = nullptr; if (!remote_build_list(sc, &filelist, rpath_clean.c_str(), lpath_clean.c_str())) return false; @@ -720,8 +736,8 @@ static bool copy_remote_dir_local(SyncConnection& sc, const char* rpath, const c while (ci) { copyinfo* next = ci->next; if (ci->flag == 0) { - fprintf(stderr, "pull: %s -> %s\n", ci->src, ci->dst); - if (!sync_recv(sc, ci->src, ci->dst, false)) { + sc.Print(android::base::StringPrintf("pull: %s -> %s", ci->src, ci->dst)); + if (!sync_recv(sc, ci->src, ci->dst)) { return false; } @@ -736,20 +752,22 @@ static bool copy_remote_dir_local(SyncConnection& sc, const char* rpath, const c ci = next; } - fprintf(stderr, "%d file%s pulled. %d file%s skipped.\n", - pulled, (pulled == 1) ? "" : "s", - skipped, (skipped == 1) ? "" : "s"); + sc.Print(android::base::StringPrintf("%s: %d file%s pulled. %d file%s skipped.%s\n", + rpath, + pulled, (pulled == 1) ? "" : "s", + skipped, (skipped == 1) ? "" : "s", + sc.TransferRate().c_str())); return true; } -bool do_sync_pull(const char* rpath, const char* lpath, bool show_progress, int copy_attrs) { +bool do_sync_pull(const char* rpath, const char* lpath, int copy_attrs) { SyncConnection sc; if (!sc.IsValid()) return false; unsigned mode, time; if (!sync_stat(sc, rpath, &time, &mode, nullptr)) return false; if (mode == 0) { - fprintf(stderr, "adb: remote object '%s' does not exist\n", rpath); + sc.Error("remote object '%s' does not exist", rpath); return false; } @@ -764,25 +782,24 @@ bool do_sync_pull(const char* rpath, const char* lpath, bool show_progress, int lpath = path_holder.c_str(); } } - if (!sync_recv(sc, rpath, lpath, show_progress)) { + if (!sync_recv(sc, rpath, lpath)) { return false; } else { if (copy_attrs && set_time_and_mode(lpath, time, mode)) { return false; } } + sc.Print("\n"); return true; } else if (S_ISDIR(mode)) { return copy_remote_dir_local(sc, rpath, lpath, copy_attrs); } - fprintf(stderr, "adb: remote object '%s' not a file or directory\n", rpath); + sc.Error("remote object '%s' not a file or directory", rpath); return false; } bool do_sync_sync(const std::string& lpath, const std::string& rpath, bool list_only) { - fprintf(stderr, "syncing %s...\n", rpath.c_str()); - SyncConnection sc; if (!sc.IsValid()) return false; diff --git a/adb/file_sync_service.h b/adb/file_sync_service.h index 67ed3fc14b..7c4d554463 100644 --- a/adb/file_sync_service.h +++ b/adb/file_sync_service.h @@ -64,9 +64,9 @@ union syncmsg { void file_sync_service(int fd, void* cookie); bool do_sync_ls(const char* path); -bool do_sync_push(const char* lpath, const char* rpath, bool show_progress); +bool do_sync_push(const char* lpath, const char* rpath); 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, bool show_progress, int copy_attrs); +bool do_sync_pull(const char* rpath, const char* lpath, int copy_attrs); #define SYNC_DATA_MAX (64*1024) diff --git a/adb/line_printer.cpp b/adb/line_printer.cpp new file mode 100644 index 0000000000..81b3f0a0b7 --- /dev/null +++ b/adb/line_printer.cpp @@ -0,0 +1,161 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "line_printer.h" + +#include <stdio.h> +#include <stdlib.h> +#ifdef _WIN32 +#include <windows.h> +#else +#include <unistd.h> +#include <sys/ioctl.h> +#include <termios.h> +#include <sys/time.h> +#endif + +// Make sure printf is really adb_printf which works for UTF-8 on Windows. +#include <sysdeps.h> + +// Stuff from ninja's util.h that's needed below. +#include <vector> +using namespace std; +string ElideMiddle(const string& str, size_t width) { + const int kMargin = 3; // Space for "...". + string result = str; + if (result.size() + kMargin > width) { + size_t elide_size = (width - kMargin) / 2; + result = result.substr(0, elide_size) + + "..." + + result.substr(result.size() - elide_size, elide_size); + } + return result; +} + +LinePrinter::LinePrinter() : have_blank_line_(true), console_locked_(false) { +#ifndef _WIN32 + const char* term = getenv("TERM"); + smart_terminal_ = isatty(1) && term && string(term) != "dumb"; +#else + // Disable output buffer. It'd be nice to use line buffering but + // MSDN says: "For some systems, [_IOLBF] provides line + // buffering. However, for Win32, the behavior is the same as _IOFBF + // - Full Buffering." + setvbuf(stdout, NULL, _IONBF, 0); + console_ = GetStdHandle(STD_OUTPUT_HANDLE); + CONSOLE_SCREEN_BUFFER_INFO csbi; + smart_terminal_ = GetConsoleScreenBufferInfo(console_, &csbi); +#endif +} + +void LinePrinter::Print(string to_print, LineType type) { + if (console_locked_) { + line_buffer_ = to_print; + line_type_ = type; + return; + } + + if (smart_terminal_) { + printf("\r"); // Print over previous line, if any. + // On Windows, calling a C library function writing to stdout also handles + // pausing the executable when the "Pause" key or Ctrl-S is pressed. + } + + if (smart_terminal_ && type == ELIDE) { +#ifdef _WIN32 + CONSOLE_SCREEN_BUFFER_INFO csbi; + GetConsoleScreenBufferInfo(console_, &csbi); + + // TODO: const std::wstring to_print_wide = widen(to_print); + // TODO: wstring ElideMiddle. + to_print = ElideMiddle(to_print, static_cast<size_t>(csbi.dwSize.X)); + // We don't want to have the cursor spamming back and forth, so instead of + // printf use WriteConsoleOutput which updates the contents of the buffer, + // but doesn't move the cursor position. + COORD buf_size = { csbi.dwSize.X, 1 }; + COORD zero_zero = { 0, 0 }; + SMALL_RECT target = { + csbi.dwCursorPosition.X, csbi.dwCursorPosition.Y, + static_cast<SHORT>(csbi.dwCursorPosition.X + csbi.dwSize.X - 1), + csbi.dwCursorPosition.Y + }; + vector<CHAR_INFO> char_data(csbi.dwSize.X); + for (size_t i = 0; i < static_cast<size_t>(csbi.dwSize.X); ++i) { + // TODO: UnicodeChar instead of AsciiChar, to_print_wide[i]. + char_data[i].Char.AsciiChar = i < to_print.size() ? to_print[i] : ' '; + char_data[i].Attributes = csbi.wAttributes; + } + // TODO: WriteConsoleOutputW. + WriteConsoleOutput(console_, &char_data[0], buf_size, zero_zero, &target); +#else + // Limit output to width of the terminal if provided so we don't cause + // line-wrapping. + winsize size; + if ((ioctl(0, TIOCGWINSZ, &size) == 0) && size.ws_col) { + to_print = ElideMiddle(to_print, size.ws_col); + } + printf("%s", to_print.c_str()); + printf("\x1B[K"); // Clear to end of line. + fflush(stdout); +#endif + + have_blank_line_ = false; + } else { + printf("%s\n", to_print.c_str()); + } +} + +void LinePrinter::PrintOrBuffer(const char* data, size_t size) { + if (console_locked_) { + output_buffer_.append(data, size); + } else { + // Avoid printf and C strings, since the actual output might contain null + // bytes like UTF-16 does (yuck). + fwrite(data, 1, size, stdout); + } +} + +void LinePrinter::PrintOnNewLine(const string& to_print) { + if (console_locked_ && !line_buffer_.empty()) { + output_buffer_.append(line_buffer_); + output_buffer_.append(1, '\n'); + line_buffer_.clear(); + } + if (!have_blank_line_) { + PrintOrBuffer("\n", 1); + } + if (!to_print.empty()) { + PrintOrBuffer(&to_print[0], to_print.size()); + } + have_blank_line_ = to_print.empty() || *to_print.rbegin() == '\n'; +} + +void LinePrinter::SetConsoleLocked(bool locked) { + if (locked == console_locked_) + return; + + if (locked) + PrintOnNewLine(""); + + console_locked_ = locked; + + if (!locked) { + PrintOnNewLine(output_buffer_); + if (!line_buffer_.empty()) { + Print(line_buffer_, line_type_); + } + output_buffer_.clear(); + line_buffer_.clear(); + } +} diff --git a/adb/line_printer.h b/adb/line_printer.h new file mode 100644 index 0000000000..3d0a5bd73b --- /dev/null +++ b/adb/line_printer.h @@ -0,0 +1,71 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef NINJA_LINE_PRINTER_H_ +#define NINJA_LINE_PRINTER_H_ + +#include <stddef.h> +#include <string> + +/// Prints lines of text, possibly overprinting previously printed lines +/// if the terminal supports it. +struct LinePrinter { + LinePrinter(); + + bool is_smart_terminal() const { return smart_terminal_; } + void set_smart_terminal(bool smart) { smart_terminal_ = smart; } + + enum LineType { + FULL, + ELIDE + }; + /// Overprints the current line. If type is ELIDE, elides to_print to fit on + /// one line. + void Print(std::string to_print, LineType type); + + /// Prints a string on a new line, not overprinting previous output. + void PrintOnNewLine(const std::string& to_print); + + /// Lock or unlock the console. Any output sent to the LinePrinter while the + /// console is locked will not be printed until it is unlocked. + void SetConsoleLocked(bool locked); + + private: + /// Whether we can do fancy terminal control codes. + bool smart_terminal_; + + /// Whether the caret is at the beginning of a blank line. + bool have_blank_line_; + + /// Whether console is locked. + bool console_locked_; + + /// Buffered current line while console is locked. + std::string line_buffer_; + + /// Buffered line type while console is locked. + LineType line_type_; + + /// Buffered console output while console is locked. + std::string output_buffer_; + +#ifdef _WIN32 + void* console_; +#endif + + /// Print the given data to the console, or buffer it if it is locked. + void PrintOrBuffer(const char *data, size_t size); +}; + +#endif // NINJA_LINE_PRINTER_H_ |
