#!/usr/bin/python
-import sys, subprocess, datetime, os
+
+import sys, datetime, os
+
PORTDIR = "/usr/portage/"
LOGFILE = "/var/log/emerge.log"
-emerge_number = 0
-times = []
green_start = "\033[32m"
+red_start = "\033[1;31m"
color_stop = "\033[m"
-def write_times(emerge_times_list):
- with open('times', 'w') as g:
+QUIET = False
+
+PACKAGES = []
+
+
+
+class package:
+ def __init__(self, name, version=0):
+ self.name = name
+ self.version = version
+ self.versions = []
+ self.emerge_time = "infinity"
+
+
+ def add_version(self, version, emerge_time, emerge_date):
+ """Add version to the class version list"""
+ self.versions.append((version, emerge_time, emerge_date))
+
+
+ def average_time(self):
+ """Return average time from class version list"""
+ total_time = 0
+ for i in self.versions:
+ total_time += i[1]
+
+ average_time = total_time/len(self.versions)
+
+ return average_time
+
+
+ def total_time(self):
+ """Return total time from class version list"""
+ total_time = 0
+ for i in self.versions:
+ total_time += i[1]
+
+ return total_time
+
+
+ def max_time(self):
+ """Return maximum time from class version list"""
+ self.versions.sort()
+
+ return self.versions[len(self.versions)-1][1]
+
+
+ def min_time(self):
+ """Return minimum time from class version list"""
+ self.versions.sort()
+
+ return self.versions[0][1]
+
+
+ def print_current_emerge(self):
+ """Function used to print all the current emerge stuff"""
+
+ print("\t" + green_start + self.name + '-' + self.version +
+ color_stop + "\n\t current time: " + give_time(self.emerge_time))
+
+
+ if len(self.versions) == 1:
+ print("\t last time: "),
+ print(give_time(self.average_time())),
+
+ elif len(self.versions) > 1:
+ print("\t average time:"),
+ print(give_time(self.average_time())),
+
+ else:
+ print("\t average time: " + green_start + "unknown\n" + color_stop),
+ return
+
+ print("\n\t " + '-'*45),
+
+ print("\n\t time to finish:"),
+
+ if type(self.emerge_time) != str:
+
+ finish_time = self.average_time() - self.emerge_time
+
+ if finish_time > 0:
+ print(give_time(finish_time))
+ else:
+ print(green_start + "any time now" + color_stop)
+ else:
+ print(green_start + "unknown" + color_stop)
+ print
+
- for i in emerge_times_list:
- line = str(i[0]) + " " + str(i[1]) + "\n"
- g.write(line)
+ def print_versions(self):
+ """This prints the emerge times for different versions in the
+ 'package' operating mode of the script"""
- g.close()
+ if QUIET == False:
+ version_length = 0
+ time_length = 0
-def organize_times(time):
- days = time/(3600*24)
- hours = (days - int(days))*24
- minutes = (hours - int(hours))*60
- seconds = (minutes - int(minutes))*60
+ for p in self.versions:
+ if len(p[0]) > version_length:
+ version_length = len(p[0])
+
+ if len(give_time(p[1], True)) > time_length:
+ time_length = len(give_time(p[1], True))
+
+ for p in self.versions:
+
+ pad = time_length - len(give_time(p[1], True))
+
+ print('-'*90 + "\n" +
+ green_start + self.name + (p[0]).ljust(version_length) +
+ color_stop + " >>> " + (give_time(p[1])) + " "*pad +
+ " >>> " + give_date(p[2]))
+
+ print('-'*90 + "\n" + "Package " + green_start +
+ self.name + color_stop + " emerged " +
+ str(len(self.versions)) + " times.")
+
+ print
+
+
+ def print_pretended_times(self):
+ """This is used the print all the pretended times"""
+
+ if QUIET == False:
+ print("\t" + green_start + self.name + '-' + self.version + color_stop),
+
+ if len(self.versions) > 1:
+ aver_time = self.average_time()
+
+ if QUIET == False:
+ print("\n\taverage time: " + give_time(aver_time))
+
+ return aver_time
+
+ else:
+ if QUIET == False:
+ print("\n\t no previous emerges")
+
+ return 0
+
+
+ def print_min_max_ave(self):
+ maxi = self.max_time()
+ mini = self.min_time()
+ average = self.average_time()
+ total = self.total_time()
+
+ print("Max time:\t" + give_time(maxi) +
+ "\nMin time:\t" + give_time(mini) +
+ "\nAverage time:\t" + give_time(average) +
+ "\nIn total spent:\t" + give_time(total) +
+ "emerging " + green_start + self.name + color_stop)
+
+
+
+def give_time(time, nocolor=False):
+ """Converts time in seconds to human readable form"""
+ global green_start, color_stop
+
+ if green_start == "":
+ nocolor = False
+
+ if nocolor == True:
+ green_start = ""
+ color_stop = ""
+
+ if type(time) == str:
+ return(green_start + "unknown" + color_stop)
+
+
+ days = time/(3600.0*24.0)
+ hours = (days - int(days))*24.0
+ minutes = (hours - int(hours))*60.0
+ seconds = (minutes - int(minutes))*60.0
days = int(days)
hours = int(hours)
minutes = int(minutes)
seconds = int(round(seconds))
+ pdays = str()
+ phours = str()
+ pminutes = str()
+ pseconds = str()
+
if days > 0:
- print_days = (green_start + str(days) + color_stop + " day")
+ pdays = (green_start + str(days) +
+ color_stop + " day ")
if days != 1:
- print_days += "s"
- print print_days,
+ pdays = (green_start + str(days) +
+ color_stop + " days ")
if hours > 0:
- print_hours = (green_start + str(hours) + color_stop + " hour")
+ phours = (green_start + str(hours) +
+ color_stop + " hour ")
if hours != 1:
- print_hours += "s"
- print print_hours,
+ phours = (green_start + str(hours) +
+ color_stop + " hours ")
if minutes > 0:
- print_minutes = (green_start + str(minutes) + color_stop + " minute")
+ pminutes = (green_start + str(minutes) +
+ color_stop + " minute ")
if minutes != 1:
- print_minutes += "s"
- print print_minutes,
+ pminutes = (green_start + str(minutes) +
+ color_stop + " minutes ")
- printable_sec = (green_start + str(seconds) + color_stop + " second")
+ pseconds = (green_start + str(seconds) +
+ color_stop + " second ")
if seconds != 1:
- printable_sec += "s"
- print printable_sec,
+ pseconds = (green_start + str(seconds) +
+ color_stop + " seconds ")
+ if nocolor == True:
+ green_start = "\033[32m"
+ color_stop = "\033[m"
-def date_printer(package, emerge_start, emerge_time):
- date = datetime.datetime.fromtimestamp(emerge_start)
+ return (pdays + phours + pminutes + pseconds)
- year = str(date.year)
- month = str(date.month)
- day = str(date.day)
- weekday = date.weekday()
- if weekday == 0: weekday = 'Mon '
- if weekday == 1: weekday = 'Tue '
- if weekday == 2: weekday = 'Wed '
- if weekday == 3: weekday = 'Thu '
- if weekday == 4: weekday = 'Fri '
- if weekday == 5: weekday = 'Sat '
- if weekday == 6: weekday = 'Sun '
- hour = str(date.hour)
- minute = str(date.minute)
- second = str(date.second)
+def give_date(emerge_date):
+ """Returns a date string from a standard POSIX time"""
+ date = datetime.datetime.fromtimestamp(emerge_date)
- date = weekday + "{:%d.%m.%Y %H:%M:%S}".format(date)
+ date = "{:%d.%m.%Y %H:%M:%S}".format(date)
return date
+
+def open_log():
+ """Attempt to open the LOGFILE."""
+
+ try:
+ f = open(LOGFILE, 'r')
+ except IOError as detail:
+ print detail
+ sys.exit(1)
+
+ return f
+
+
+
+def search_log_for_package(package_class):
+ """Searchs emerge log for given package and adds all found
+ versions with their emerge times to the class"""
+
+ log = open_log()
+
+ for line in log:
+ if ((">>>" in line) and ("emerge" in line)):
+ if package_class.name in line:
+
+ version = line.partition(package_class.name)[2].partition(' ')[0]
+ digit = version.strip('-')[0].isdigit()
+
+ if digit:
+ time_string = line.partition(">>>")
+ start_time = float(time_string[0].strip().strip(':'))
+
+ elif ((":::" in line) and ("completed emerge" in line)):
+
+ if package_class.name in line:
+ if digit:
+ time_string = line.partition(":::")
+ stop_time = float(time_string[0].strip().strip(':'))
+
+ emerge_time = stop_time - start_time
+
+ package_class.add_version(version, emerge_time, start_time)
+
+
+
def get_package(name):
+ """Take the user-input package name and search for it
+ in PORTDIR. """
+
dirlist = os.listdir(PORTDIR)
possible_package = []
+
+ # If the given name is in the format xxx/zzz
+ # assume that xxx is the package group
if '/' in name:
group = name.partition('/')[0]
pkg = name.partition('/')[2]
if pkg in dirs:
possible_package.append(name)
+
+ # Go through the directory listing searching for anything
+ # that matches the given name
for i in dirlist:
directory = PORTDIR + i
if os.path.isdir(directory):
if name in dirs:
possible_package.append(i + '/' + name)
+
if len(possible_package) > 1:
print("Multiple packages found for '" + name + "'.")
print("Possible packages: ")
for value in possible_package:
print("\t" + value)
+
elif len(possible_package) == 1:
package = possible_package[0]
return package
+
else:
print("No package '" + name + "' found")
+
sys.exit(1)
-def print_times(package, times, silent):
- times.sort()
- times.reverse()
- if silent == True:
- if emerge_number == 0:
- print("\t no previous emerges found for this package"),
+def list_pretended():
+ """List all the pretended packages given by emerge -p
+ output. Create a class out of each of those packages and add them
+ to the list."""
- elif emerge_number == 1:
- print("\t previous emerge time"),
- organize_times(times[0][0])
- print("(only one emerge previously)"),
+ log = open_log()
- else:
- print("\t average emerge time: "),
- all_times = 0
- for i in times:
- all_times += i[0]
+ for line in sys.stdin:
+ if "[ebuild" in line:
+ full_name = line.partition("] ")[2].partition(' ')[0]
- organize_times(all_times/len(times))
+ version = full_name.partition('/')[2].partition('-')[2]
+ while not version[0].isdigit():
+ version = version.partition('-')[2]
+ package_name = full_name[:-len(version)-1]
- print('\n')
- return
+ PACKAGES.append(package(package_name, version))
- if emerge_number == 1:
- print(green_start + package + color_stop + " emerged once")
- else:
- print(green_start + package + color_stop + " emerged " + green_start +
- str(emerge_number) + color_stop + " times\n")
+def list_emerge_processes():
+ """Look for the ebuild process with ps. If the process is found parse
+ the command for the package. With this package search the LOGFILE for
+ the emerge startup time."""
- print "Max time:\t",
- organize_times(times[0][0])
- print "at", times[0][1]
+ f = open_log()
- print "Min time:\t",
- organize_times(times[len(times)-1][0])
- print "at", times[len(times)-1][1]
+ now = datetime.datetime.today()
- all_times = 0
- for i in times:
- all_times += i[0]
+ for i in os.popen("ps ax"):
+ if (("ebuild.sh" in i) and ("/bin/bash" not in i)):
+ pack = i.partition('[')[2].partition(']')[0]
- print "Average time\t",
- organize_times(all_times/len(times))
- print
+ version = pack.partition('/')[2].partition('-')[2]
- print "In total spent\t",
- organize_times(all_times)
- print("emerging " + green_start +
- package + color_stop)
+ while not version[0].isdigit():
+ version = version.partition('-')[2]
+ package_name = pack[:-len(version)-1]
-def open_log():
- try:
- f = open(LOGFILE, 'r')
- except IOError:
- print "Permission denied: ", LOGFILE
- sys.exit(1)
- finally:
- return f
+ PACKAGES.append(package(package_name, version))
-def list_emerge_processes(f):
+ if len(PACKAGES) == 0:
+ print "No current emerge process found."
- packages = []
- for i in os.popen("ps ax"):
- if (("ebuild" in i) and ("/bin/bash" not in i)):
- pack = i.partition('[')[2].partition(']')[0]
+ return 1
- packages.append([pack, 0])
for line in f:
- for p in packages:
- if ((p[0] in line) and (">>>" in line) and ("emerge" in line)):
-
- time = float(line.partition(' ')[0].strip(":"))
- if time > p[1]:
- p[1] = time
-
- a = 1
- for p in packages:
- now = datetime.datetime.today()
- timestamp = datetime.datetime.fromtimestamp(p[1])
- difference = now - timestamp
-
- if difference.total_seconds() < 12*60*60:
- if a:
- print("Currently emerging: ")
- a = 0
- print("\t" + green_start + p[0] + color_stop),
- print('\n\t current emerge time: '),
- organize_times(difference.total_seconds())
- print
+ if ((">>>" in line) and ("emerge" in line)):
+ for p in PACKAGES:
+ difference = 0
- name = (p[0].partition('-')[0] + '-' +
- p[0].partition('-')[2].partition('-')[0])
- main_loop(f, name, True)
+ if (p.name + '-' + p.version in line):
+ time = float(line.partition(' ')[0].strip(":"))
+ timestamp = datetime.datetime.fromtimestamp(time)
+ difference = (now - timestamp).total_seconds()
-def main_loop(f, package, silent):
- global emerge_number
- emerge_number = 0
- f.seek(0)
+ if ((difference < p.emerge_time) or
+ (p.emerge_time == "infinity")):
- # MAIN LOOP
- for line in f:
- if package in line:
- st = line.split(' ')
- for string in st:
- if ((package in string) and (len(string) > len(package)+1)
- and (string[len(package)+1].isdigit())):
- full_package = st[st.index(string)]
+ p.emerge_time = difference
- if ((">>>" in line) and ("emerge" in line)):
- time_string = line.partition(">>>")
- time = float(time_string[0].strip().strip(":"))
+ return 0
- try:
- if (":::" in line) and ("completed emerge" in line):
- emerge_number += 1
- time_string = line.partition(":::")
- time2 = float(time_string[0].strip().strip(":"))
- emerge_time = time2-time
+def main(status, user_package=None):
+ """Main function. Hanlde all the different modes of operation."""
- date = date_printer(full_package, time, emerge_time)
+ if status == "package":
+ user_package = get_package(user_package)
- if silent == False:
- print(str(len(times)+1) + "."),
- print(green_start + full_package + color_stop + " >>> "
- + date + " >>> "),
+ pack = package(user_package)
- organize_times(emerge_time)
+ search_log_for_package(pack)
- print("\n" + '-'*90)
+ if len(pack.versions) != 0:
+ pack.print_versions()
+ pack.print_min_max_ave()
- times.append((emerge_time, date))
+ else:
+ print("Package " + green_start + pack.name +
+ color_stop + " has never been emerged.")
- except IndexError: pass
- print_times(package, times, silent)
+ elif status == "current":
+ if list_emerge_processes():
+ return
+ print "Currently emerging:"
-def main(status):
- global emerge_number
+ for p in PACKAGES:
+ search_log_for_package(p)
+ p.print_current_emerge()
- f = open_log()
- if status == 'current':
- list_emerge_processes(f)
- return
- else:
- pass
+ elif status == "pretended":
+ list_pretended()
- package = get_package(package_name)
+ print "This is how long these packages would take to emerge"
- print('-'*90)
+ total_pretended_time = 0
- main_loop(f, package, False)
+ for p in PACKAGES:
+ search_log_for_package(p)
- if emerge_number == 0:
- print("Package '" + package + "' has never been emerged")
- sys.exit(1)
+ total_pretended_time += p.print_pretended_times()
+
+ print
- f.close()
+ print("Total emerge time of " + green_start + str(len(PACKAGES)) +
+ color_stop + " package(s): "+ give_time(total_pretended_time))
+
+
+def usage():
+ usage = """Usage: emerge-timer.py [package] [options]
+
+Calculate emerge times from emerge log.
+
+Options:
+\t-c, --current \t Show time until currently compiling package finishes
+\t-p, --pretended Calculate compile time from piped 'emerge -p' output
+\t-h, --help \t Show this helpscreen
+\t-q, --quiet \t Be less verbose
+\t--no-color \t Use colorless output
+\t--simulate \t Do a simulation run"""
+
+ print usage
+
+ sys.exit(0)
if __name__ == "__main__":
+ # Set the default mode as "package"
+ mode = "package"
+ input_package = None
+ simulation = False
+
+ for arg in sys.argv[1:]:
+
+ if arg == "-p" or arg == "--pretended":
+ mode = "pretended"
+
+ if arg == "-c" or arg == "--current":
+ mode = "current"
+
+ if arg == "-h" or arg == "--help":
+ usage()
+
+ if arg == "-q" or arg == "--quiet":
+ QUIET = True
+
+ if arg == "--no-color":
+ green_start = ""
+ color_stop = ""
+
+ if arg == "--simulate":
+ simulation = True
+
+
if len(sys.argv) > 1:
- package_name = sys.argv[1]
- main(0)
+ input_package = sys.argv[1]
else:
- main('current')
- sys.exit(1)
+ usage()
+
+ if simulation == True:
+
+ print(red_start + "\n" + '*'*25 + "\n" + "THIS IS A SIMULATION RUN\n"
+ + '*'*25 + "\n")
+
+ print("Beginning 'package' mode check")
- write_times(times)
+ print("Checking for one emerge\n" + color_stop)
+
+ LOGFILE = "simulate/fake_emerge.log"
+ PORTDIR = "simulate/"
+
+ main("package", "first_fake_package")
+
+ print(red_start + "\nChecking for three emerges\n" + color_stop)
+
+ main("package", "second_fake_package")
+
+ print(red_start + "\n'package' mode check complete\n")
+
+ print(30*'*')
+
+ print("\nBeginning 'current' mode check")
+ print("Current emerge with no emerge process\n" + color_stop)
+
+ main("current", None)
+
+ print(red_start + "\nCurrent emerge with emerge processes\n" + color_stop)
+
+ PACKAGES.append(package("test-group/second_fake_package", "2.9-r2"))
+ PACKAGES.append(package("test-group/first_fake_package", "1.10.2-r1"))
+
+ main("current", None)
+
+ print(red_start + "\nCurrent emerge with incomplete emerge log" +
+ "(causes error in some cases)\n" + color_stop)
+
+ PACKAGES = []
+ PACKAGES.append(package("test-group/third_fake_package", "2.9-r2"))
+
+ main("current", None)
+
+ print(red_start + "\n" + '*'*20 + "\n" + "SIMULATION FINISHED\n" +
+ '*'*20 + color_stop)
+
+
+ else:
+ main(mode, input_package)