4 import sys, datetime, os
7 PORTDIR = "/usr/portage/"
8 LOGFILE = "/var/log/emerge.log"
11 green_start = "\033[32m"
12 red_start = "\033[1;31m"
20 return green_start + string + color_stop
23 return red_start + string + color_stop
27 def __init__(self, name, version=0):
29 self.version = version
31 self.emerge_time = "infinity"
36 def add_version(self, version, emerge_time, emerge_date):
37 """Add version to the class version list"""
38 self.versions.append((version, emerge_time, emerge_date))
41 def seek_versions(self, whatToSeek, Input):
43 if whatToSeek == "version":
44 for i in self.versions:
49 def average_time(self):
50 """Return average time from class version list"""
52 for i in self.versions:
55 average_time = total_time/len(self.versions)
61 """Return total time from class version list"""
63 for i in self.versions:
69 def min_max_time(self, setting):
70 """Return maximum or minimum time from class version list"""
73 for i in self.versions:
74 emerge_times.append(i[1])
79 return emerge_times[-1]
80 elif setting == "min":
81 return emerge_times[0]
86 def print_current_emerge(self):
87 """Function used to print all the current emerge stuff"""
89 print("\t" + GREEN(self.name + '-' + self.version) +
90 "\n\t current time: " + give_time(self.emerge_time))
93 if len(self.versions) == 1:
94 print("\t last time: "),
95 print(give_time(self.average_time())),
97 elif len(self.versions) > 1:
98 print("\t average time:"),
99 print(give_time(self.average_time())),
102 print("\t average time: " + GREEN("unknown\n")),
105 print("\n\t " + '-'*45),
107 print("\n\t time to finish:"),
109 if type(self.emerge_time) != str:
111 finish_time = self.average_time() - self.emerge_time
114 print(give_time(finish_time))
116 print(GREEN("any time now"))
118 print(GREEN("unknown"))
122 def print_versions(self):
123 """This prints the emerge times for different versions in the
124 'package' operating mode of the script"""
131 for p in self.versions:
132 if len(p[0]) > version_length:
133 version_length = len(p[0])
135 if len(give_time(p[1], True)) > time_length:
136 time_length = len(give_time(p[1], True))
138 # Create the data for plotting in the format (emerge time, emerge date)
139 self.plotData.append((p[1], p[2]))
141 dots = (version_length + time_length + len(self.name)
142 + len(give_date(self.versions[0][2])) + 14)
144 for p in self.versions:
146 pad = time_length - len(give_time(p[1], True))
149 p_time = give_time(p[1])
150 p_date = give_date(p[2])
152 print('-' * dots + "\n" +
153 GREEN(name + (p[0]).ljust(version_length))
154 + " >>> " + p_time + " "*pad + " >>> " + p_date)
156 print("\n" + "Package " + GREEN(self.name) + " emerged"),
158 if len(self.versions) > 1:
159 print(str(len(self.versions)) + " times.\n")
160 elif len(self.versions) == 1:
165 def print_pretended_times(self):
166 """This is used the print all the pretended times"""
169 print("\t" + GREEN(self.name + '-' + self.version)),
171 if len(self.versions) > 1:
172 aver_time = self.average_time()
175 print("\n\taverage time: " + give_time(aver_time))
181 print("\n\t no previous emerges")
186 def print_min_max_ave(self):
188 if len(self.versions) == 1:
191 maxi = self.min_max_time("max")
192 mini = self.min_max_time("min")
193 average = self.average_time()
194 total = self.total_time()
196 print("Max time:\t" + give_time(maxi) + "on " +
197 give_date(self.seek_versions("version", maxi)) +
198 "\nMin time:\t" + give_time(mini) + "on " +
199 give_date(self.seek_versions("version", mini)) +
200 "\nAverage time:\t" + give_time(average) +
201 "\n\nIn total spent:\t" + give_time(total) +
202 "emerging " + GREEN(self.name))
204 def plotToScreen(self):
208 for i in self.plotData:
209 dates.append(datetime.date.fromtimestamp(i[1]))
214 plt.plot_date(dates, times, xdate=True, ydate=False)
216 plt.ylabel("Emerge time [s]")
217 plt.suptitle(self.name)
224 def give_time(time, nocolor=False):
225 """Converts time in seconds to human readable form"""
226 global green_start, color_stop
228 if green_start == "":
235 if type(time) == str:
236 return(GREEN("unknown"))
239 days = time/(3600.0*24.0)
240 hours = (days - int(days))*24.0
241 minutes = (hours - int(hours))*60.0
242 seconds = (minutes - int(minutes))*60.0
246 minutes = int(minutes)
247 seconds = int(round(seconds))
255 pdays = (GREEN(str(days)) + " day ")
257 pdays = (GREEN(str(days)) + " days ")
260 phours = (GREEN(str(hours)) + " hour ")
262 phours = (GREEN(str(hours)) + " hours ")
265 pminutes = (GREEN(str(minutes)) + " minute ")
267 pminutes = (GREEN(str(minutes)) + " minutes ")
270 pseconds = (GREEN(str(seconds)) + " second ")
272 pseconds = (GREEN(str(seconds)) + " seconds ")
275 green_start = "\033[32m"
276 color_stop = "\033[m"
278 return (pdays + phours + pminutes + pseconds)
282 def give_date(emerge_date):
283 """Returns a date string from a standard POSIX time"""
284 date = datetime.datetime.fromtimestamp(emerge_date)
286 date = "{:%d.%m.%Y %H:%M:%S}".format(date)
293 """Attempt to open the LOGFILE."""
296 f = open(LOGFILE, 'r')
297 except IOError as detail:
305 def search_log_for_package(package_class):
306 """Searchs emerge log for given package and adds all found
307 versions with their emerge times to the class"""
312 if ((">>>" in line) and ("emerge" in line)):
313 if package_class.name in line:
315 version = line.partition(package_class.name)[2].partition(' ')[0]
316 digit = version.strip('-')[0].isdigit()
319 time_string = line.partition(">>>")
320 start_time = float(time_string[0].strip().strip(':'))
322 elif ((":::" in line) and ("completed emerge" in line)):
324 if package_class.name in line:
326 time_string = line.partition(":::")
327 stop_time = float(time_string[0].strip().strip(':'))
329 emerge_time = stop_time - start_time
331 package_class.add_version(version, emerge_time, start_time)
335 def search_log_for_all_packages():
336 """Goes through the emerge.log and lists all packages in there"""
342 total_emerge_time = 0
346 if ((">>>" in line) and ("emerge" in line)):
347 pack = line.partition(')')[2].strip().partition(' ')[0]
348 start_time = float(line.partition(':')[0])
350 all_packages.append((pack, start_time))
352 elif ((":::" in line) and ("completed emerge" in line)):
353 for p in all_packages:
355 print("\t" + give_date(p[1]) + " >>> " + GREEN(p[0]))
359 all_packages.pop(all_packages.index(p))
362 print("\n" + GREEN(str(emerge_number)) + " emerges in total found.")
366 def get_package(name):
367 """Take the user-input package name and search for it
370 dirlist = os.listdir(PORTDIR)
371 possible_package = []
374 # If the given name is in the format xxx/zzz
375 # assume that xxx is the package group
377 group = name.partition('/')[0]
378 pkg = name.partition('/')[2]
379 directory = PORTDIR + group
382 dirs = os.listdir(directory)
384 possible_package.append(name)
387 # Go through the directory listing searching for anything
388 # that matches the given name
390 directory = PORTDIR + i
391 if os.path.isdir(directory):
392 dirs = os.listdir(directory)
394 possible_package.append(i + '/' + name)
397 if len(possible_package) > 1:
398 print("Multiple packages found for '" + name + "'.")
399 print("Possible packages: ")
400 for value in possible_package:
404 elif len(possible_package) == 1:
405 package = possible_package[0]
410 print("No package '" + name + "' found")
417 def list_pretended():
418 """List all the pretended packages given by emerge -p
419 output. Create a class out of each of those packages and add them
424 for line in sys.stdin:
425 if "[ebuild" in line:
426 full_name = line.partition("] ")[2].partition(' ')[0]
428 version = full_name.partition('/')[2].partition('-')[2]
429 while not version[0].isdigit():
430 version = version.partition('-')[2]
431 package_name = full_name[:-len(version)-1]
433 PACKAGES.append(package(package_name, version))
437 def list_emerge_processes():
438 """Look for the ebuild process with ps. If the process is found parse
439 the command for the package. With this package search the LOGFILE for
440 the emerge startup time."""
444 now = datetime.datetime.today()
446 for i in os.popen("ps ax"):
447 if (("ebuild.sh" in i) and ("/bin/bash" not in i)):
448 pack = i.partition('[')[2].partition(']')[0]
450 version = pack.partition('/')[2].partition('-')[2]
452 while not version[0].isdigit():
453 version = version.partition('-')[2]
455 package_name = pack[:-len(version)-1]
457 PACKAGES.append(package(package_name, version))
460 if len(PACKAGES) == 0:
461 print "No current emerge process found."
467 if ((">>>" in line) and ("emerge" in line)):
471 if (p.name + '-' + p.version in line):
473 time = float(line.partition(' ')[0].strip(":"))
475 timestamp = datetime.datetime.fromtimestamp(time)
476 difference = (now - timestamp).total_seconds()
478 if ((difference < p.emerge_time) or
479 (p.emerge_time == "infinity")):
481 p.emerge_time = difference
489 print "These emerge syncs found"
490 print "\tDate Server"
491 print "\t------------------------------"
495 if "=== Sync completed with" in line:
496 time = float(line.partition(' ')[0].strip(":"))
497 server = line.rpartition(' ')[2]
499 print("\t" + GREEN(give_date(time)) +
503 print("\n" + GREEN(str(a)) + " emerge syncs found. ")
506 def main(status, user_package=None):
508 _main(status, user_package)
513 def _main(status, user_package=None):
514 """Main function. Handle all the different modes of operation."""
516 if status == "package":
517 for p in user_package:
518 pack = get_package(p)
522 search_log_for_package(pack)
524 if len(pack.versions) != 0:
525 pack.print_versions()
526 pack.print_min_max_ave()
532 print("Package " + GREEN(pack.name)
533 + " has never been emerged.")
536 elif status == "sync":
541 elif status == "list":
542 search_log_for_all_packages()
546 elif status == "current":
547 if list_emerge_processes():
550 print "Currently emerging:"
553 search_log_for_package(p)
554 p.print_current_emerge()
557 elif status == "pretended":
560 print "This is how long these packages would take to emerge"
562 total_pretended_time = 0
565 search_log_for_package(p)
567 total_pretended_time += p.print_pretended_times()
571 print("Total emerge time of " + GREEN(str(len(PACKAGES)))
572 + " package(s): "+ give_time(total_pretended_time))
576 usage = """Usage: emerge-timer.py [package] [options]
578 Calculate emerge times from emerge log.
581 \t-c, --current \t Show time until currently compiling package finishes
582 \t-p, --pretended Calculate compile time from piped 'emerge -p' output
583 \t-l, --list \t List all emerged packages
584 \t-s, --sync \t Show emerge sync history
585 \t-h, --help \t Show this helpscreen
586 \t-q, --quiet \t Be less verbose
587 \t--no-color \t Use colorless output
588 \t--plot \t\t Plot emerge times into a 2D scatter plot
589 \t\t\t (needs matlibplot installed for this to work)"""
597 if __name__ == "__main__":
599 # Set the default mode as "package"
601 input_packages = None
603 matplotWorking = False
605 for arg in sys.argv[1:]:
607 if arg == "-p" or arg == "--pretended":
610 if arg == "-c" or arg == "--current":
613 if arg == "-h" or arg == "--help":
616 if arg == "-l" or arg == "--list":
619 if arg == "-s" or arg == "--sync":
622 if arg == "-q" or arg == "--quiet":
625 sys.argv.pop(sys.argv.index(arg))
629 import matplotlib.pyplot as plt
630 matplotWorking = True
632 print(RED("Cannot initialize matplotlib!"))
633 print(RED("Check that you have properly installed matplotlib.\n"))
634 matplotWorking = False
636 sys.argv.pop(sys.argv.index(arg))
638 if arg == "--no-color":
642 sys.argv.pop(sys.argv.index(arg))
644 if arg == "--simulate":
648 if len(sys.argv) > 1:
649 input_packages = sys.argv[1:]
653 main(mode, input_packages)