]> git.itanic.dy.fi Git - emerge-timer/blob - emerge-timer.py
Add the ability to search emerge times for multiple packages at the
[emerge-timer] / emerge-timer.py
1 #!/usr/bin/python
2
3
4 import sys, datetime, os
5
6
7 PORTDIR = "/usr/portage/"
8 LOGFILE = "/var/log/emerge.log"
9
10
11 green_start = "\033[32m"
12 red_start = "\033[1;31m"
13 color_stop = "\033[m"
14
15 QUIET = False
16
17 PACKAGES = []
18
19
20
21 class package:
22     def __init__(self, name, version=0):
23         self.name = name
24         self.version = version
25         self.versions = []
26         self.emerge_time = "infinity"
27
28
29     def add_version(self, version, emerge_time, emerge_date):
30         """Add version to the class version list"""
31         self.versions.append((version, emerge_time, emerge_date))
32
33
34     def average_time(self):
35         """Return average time from class version list"""
36         total_time = 0
37         for i in self.versions:
38             total_time += i[1]
39
40         average_time = total_time/len(self.versions)
41
42         return average_time
43
44
45     def total_time(self):
46         """Return total time from class version list"""
47         total_time = 0
48         for i in self.versions:
49             total_time += i[1]
50
51         return total_time
52
53
54     def max_time(self):
55         """Return maximum time from class version list"""
56
57         emerge_times = []
58         for i in self.versions:
59             emerge_times.append(i[1])
60
61         emerge_times.sort()
62
63         return emerge_times[-1]
64
65
66     def min_time(self):
67         """Return minimum time from class version list"""
68
69         emerge_times = []
70         for i in self.versions:
71             emerge_times.append(i[1])
72
73         emerge_times.sort()
74
75         return emerge_times[0]
76
77
78
79     def print_current_emerge(self):
80         """Function used to print all the current emerge stuff"""
81
82         print("\t" + green_start + self.name + '-' + self.version +
83               color_stop + "\n\t current time: " + give_time(self.emerge_time))
84
85
86         if len(self.versions) == 1:
87             print("\t last time:   "),
88             print(give_time(self.average_time())),
89
90         elif len(self.versions) > 1:
91             print("\t average time:"),
92             print(give_time(self.average_time())),
93
94         else:
95             print("\t average time: " + green_start + "unknown\n" + color_stop),
96             return
97
98         print("\n\t " + '-'*45),
99
100         print("\n\t time to finish:"),
101
102         if type(self.emerge_time) != str:
103
104             finish_time = self.average_time() - self.emerge_time
105
106             if finish_time > 0:
107                 print(give_time(finish_time))
108             else:
109                 print(green_start + "any time now" + color_stop)
110         else:
111             print(green_start + "unknown" + color_stop)
112         print
113
114
115     def print_versions(self):
116         """This prints the emerge times for different versions in the
117         'package' operating mode of the script"""
118
119         if QUIET == False:
120
121             version_length = 0
122             time_length = 0
123
124             for p in self.versions:
125                 if len(p[0]) > version_length:
126                     version_length = len(p[0])
127
128                 if len(give_time(p[1], True)) > time_length:
129                     time_length = len(give_time(p[1], True))
130
131             dots =  (version_length + time_length + len(self.name)
132                      + len(give_date(self.versions[0][2])) + 14)
133
134             for p in self.versions:
135
136                 pad = time_length - len(give_time(p[1], True))
137
138                 name = self.name
139                 p_time = give_time(p[1])
140                 p_date = give_date(p[2])
141
142                 print('-' * dots + "\n" +
143                       green_start + name + (p[0]).ljust(version_length) +
144                       color_stop + "  >>>  " + p_time + " "*pad +
145                       "  >>>  " + p_date)
146
147         print("\n" + "Package " + green_start +
148               self.name + color_stop + " emerged " +
149               str(len(self.versions)) + " times.\n")
150
151
152
153     def print_pretended_times(self):
154         """This is used the print all the pretended times"""
155
156         if QUIET == False:
157             print("\t" + green_start + self.name + '-' + self.version + color_stop),
158
159         if len(self.versions) > 1:
160             aver_time = self.average_time()
161
162             if QUIET == False:
163                 print("\n\taverage time: " + give_time(aver_time))
164
165             return aver_time
166
167         else:
168             if QUIET == False:
169                 print("\n\t no previous emerges")
170
171             return 0
172
173
174     def print_min_max_ave(self):
175         maxi = self.max_time()
176         mini = self.min_time()
177         average = self.average_time()
178         total = self.total_time()
179
180         print("Max time:\t" + give_time(maxi) +
181               "\nMin time:\t" + give_time(mini) +
182               "\nAverage time:\t" + give_time(average) +
183               "\nIn total spent:\t" + give_time(total) +
184               "emerging " + green_start + self.name + color_stop)
185
186
187
188 def give_time(time, nocolor=False):
189     """Converts time in seconds to human readable form"""
190     global green_start, color_stop
191
192     if green_start == "":
193         nocolor = False
194
195     if nocolor == True:
196         green_start = ""
197         color_stop = ""
198
199     if type(time) == str:
200         return(green_start + "unknown" + color_stop)
201
202
203     days = time/(3600.0*24.0)
204     hours = (days - int(days))*24.0
205     minutes = (hours - int(hours))*60.0
206     seconds = (minutes - int(minutes))*60.0
207
208     days = int(days)
209     hours = int(hours)
210     minutes = int(minutes)
211     seconds = int(round(seconds))
212
213     pdays = str()
214     phours = str()
215     pminutes = str()
216     pseconds = str()
217
218     if days > 0:
219         pdays = (green_start + str(days) +
220                  color_stop + " day ")
221         if days != 1:
222             pdays = (green_start + str(days) +
223                          color_stop + " days ")
224
225     if hours > 0:
226         phours = (green_start + str(hours) +
227                   color_stop + " hour ")
228         if hours != 1:
229             phours = (green_start + str(hours) +
230                           color_stop + " hours ")
231
232     if minutes > 0:
233         pminutes = (green_start + str(minutes) +
234                     color_stop + " minute ")
235         if minutes != 1:
236             pminutes = (green_start + str(minutes) +
237                         color_stop + " minutes ")
238
239     if seconds > 0:
240         pseconds = (green_start + str(seconds) +
241                     color_stop + " second ")
242         if seconds != 1:
243             pseconds = (green_start + str(seconds) +
244                         color_stop + " seconds ")
245
246     if nocolor == True:
247         green_start = "\033[32m"
248         color_stop = "\033[m"
249
250     return (pdays + phours + pminutes + pseconds)
251
252
253
254 def give_date(emerge_date):
255     """Returns a date string from a standard POSIX time"""
256     date = datetime.datetime.fromtimestamp(emerge_date)
257
258     date = "{:%d.%m.%Y %H:%M:%S}".format(date)
259
260     return date
261
262
263
264 def open_log():
265     """Attempt to open the LOGFILE."""
266
267     try:
268         f = open(LOGFILE, 'r')
269     except IOError as detail:
270         print detail
271         sys.exit(1)
272
273     return f
274
275
276
277 def search_log_for_package(package_class):
278     """Searchs emerge log for given package and adds all found
279     versions with their emerge times to the class"""
280
281     log = open_log()
282
283     for line in log:
284         if ((">>>" in line) and ("emerge" in line)):
285             if package_class.name in line:
286
287                 version = line.partition(package_class.name)[2].partition(' ')[0]
288                 digit = version.strip('-')[0].isdigit()
289
290                 if digit:
291                     time_string = line.partition(">>>")
292                     start_time = float(time_string[0].strip().strip(':'))
293
294         elif ((":::" in line) and ("completed emerge" in line)):
295
296             if package_class.name in line:
297                 if digit:
298                     time_string = line.partition(":::")
299                     stop_time = float(time_string[0].strip().strip(':'))
300
301                     emerge_time = stop_time - start_time
302
303                     package_class.add_version(version, emerge_time, start_time)
304
305
306
307 def get_package(name):
308     """Take the user-input package name and search for it
309     in PORTDIR. """
310
311     dirlist = os.listdir(PORTDIR)
312     possible_package = []
313
314
315     # If the given name is in the format xxx/zzz
316     # assume that xxx is the package group
317     if '/' in name:
318         group = name.partition('/')[0]
319         pkg = name.partition('/')[2]
320         directory = PORTDIR + group
321
322         if group in dirlist:
323             dirs = os.listdir(directory)
324             if pkg in dirs:
325                 possible_package.append(name)
326
327
328     # Go through the directory listing searching for anything
329     # that matches the given name
330     for i in dirlist:
331         directory = PORTDIR + i
332         if os.path.isdir(directory):
333             dirs = os.listdir(directory)
334             if name in dirs:
335                 possible_package.append(i + '/' + name)
336
337
338     if len(possible_package) > 1:
339         print("Multiple packages found for '" + name + "'.")
340         print("Possible packages: ")
341         for value in possible_package:
342             print("\t" + value)
343
344
345     elif len(possible_package) == 1:
346         package = possible_package[0]
347         return package
348
349
350     else:
351         print("No package '" + name + "' found")
352
353
354     sys.exit(1)
355
356
357
358 def list_pretended():
359     """List all the pretended packages given by emerge -p
360     output. Create a class out of each of those packages and add them
361     to the list."""
362
363     log = open_log()
364
365     for line in sys.stdin:
366         if "[ebuild" in line:
367             full_name = line.partition("] ")[2].partition(' ')[0]
368
369             version = full_name.partition('/')[2].partition('-')[2]
370             while not version[0].isdigit():
371                 version = version.partition('-')[2]
372             package_name = full_name[:-len(version)-1]
373
374             PACKAGES.append(package(package_name, version))
375
376
377
378 def list_emerge_processes():
379     """Look for the ebuild process with ps. If the process is found parse
380     the command for the package. With this package search the LOGFILE for
381     the emerge startup time."""
382
383     f = open_log()
384
385     now = datetime.datetime.today()
386
387     for i in os.popen("ps ax"):
388         if (("ebuild.sh" in i) and ("/bin/bash" not in i)):
389             pack = i.partition('[')[2].partition(']')[0]
390
391             version = pack.partition('/')[2].partition('-')[2]
392
393             while not version[0].isdigit():
394                 version = version.partition('-')[2]
395
396             package_name = pack[:-len(version)-1]
397
398             PACKAGES.append(package(package_name, version))
399
400
401     if len(PACKAGES) == 0:
402         print "No current emerge process found."
403
404         return 1
405
406
407     for line in f:
408         if ((">>>" in line) and ("emerge" in line)):
409             for p in PACKAGES:
410                 difference = 0
411
412                 if (p.name + '-' + p.version in line):
413
414                     time = float(line.partition(' ')[0].strip(":"))
415
416                     timestamp = datetime.datetime.fromtimestamp(time)
417                     difference = (now - timestamp).total_seconds()
418
419                     if ((difference < p.emerge_time) or
420                         (p.emerge_time == "infinity")):
421
422                         p.emerge_time = difference
423
424     return 0
425
426
427
428 def main(status, user_package=None):
429     """Main function. Hanlde all the different modes of operation."""
430
431     if status == "package":
432         for p in user_package:
433             pack = get_package(p)
434
435             pack = package(pack)
436
437             search_log_for_package(pack)
438
439             if len(pack.versions) != 0:
440                 pack.print_versions()
441                 pack.print_min_max_ave()
442
443             else:
444                 print("Package " + green_start + pack.name +
445                       color_stop + " has never been emerged.")
446
447             print
448
449
450     elif status == "current":
451         if list_emerge_processes():
452             return
453
454         print "Currently emerging:"
455
456         for p in PACKAGES:
457             search_log_for_package(p)
458             p.print_current_emerge()
459
460
461     elif status == "pretended":
462         list_pretended()
463
464         print "This is how long these packages would take to emerge"
465
466         total_pretended_time = 0
467
468         for p in PACKAGES:
469             search_log_for_package(p)
470
471             total_pretended_time += p.print_pretended_times()
472
473             print
474
475         print("Total emerge time of " + green_start + str(len(PACKAGES)) +
476               color_stop + " package(s): "+ give_time(total_pretended_time))
477
478
479 def usage():
480     usage = """Usage: emerge-timer.py [package] [options]
481
482 Calculate emerge times from emerge log.
483
484 Options:
485 \t-c, --current \t Show time until currently compiling package finishes
486 \t-p, --pretended  Calculate compile time from piped 'emerge -p' output
487 \t-h, --help \t Show this helpscreen
488 \t-q, --quiet \t Be less verbose
489 \t--no-color \t Use colorless output
490 \t--simulate \t Do a simulation run"""
491
492     print usage
493
494     sys.exit(0)
495
496
497 if __name__ == "__main__":
498
499     # Set the default mode as "package"
500     mode = "package"
501     input_packages = None
502     simulation = False
503
504     for arg in sys.argv[1:]:
505
506         if arg == "-p" or arg == "--pretended":
507             mode = "pretended"
508
509         if arg == "-c" or arg == "--current":
510             mode = "current"
511
512         if arg == "-h" or arg == "--help":
513             usage()
514
515         if arg == "-q" or arg == "--quiet":
516             QUIET = True
517
518             sys.argv.pop(sys.argv.index(arg))
519
520         if arg == "--no-color":
521             green_start = ""
522             color_stop = ""
523
524             sys.argv.pop(sys.argv.index(arg))
525
526         if arg == "--simulate":
527             simulation = True
528
529
530     if len(sys.argv) > 1:
531         input_packages = sys.argv[1:]
532     else:
533         usage()
534
535     if simulation == True:
536
537         print(red_start + "\n" + '*'*25 + "\n" + "THIS IS A SIMULATION RUN\n"
538               + '*'*25 + "\n")
539
540         print("Beginning 'package' mode check")
541
542         print("Checking for one emerge\n" + color_stop)
543
544         LOGFILE = "simulate/fake_emerge.log"
545         PORTDIR = "simulate/"
546
547         main("package", "first_fake_package")
548
549         print(red_start + "\nChecking for three emerges\n" + color_stop)
550
551         main("package", "second_fake_package")
552
553         print(red_start + "\n'package' mode check complete\n")
554
555         print(30*'*')
556
557         print("\nBeginning 'current' mode check")
558         print("Current emerge with no emerge process\n" + color_stop)
559
560         main("current", None)
561
562         print(red_start + "\nCurrent emerge with emerge processes\n" + color_stop)
563
564         PACKAGES.append(package("test-group/second_fake_package", "2.9-r2"))
565         PACKAGES[0].emerge_time = 60
566         PACKAGES.append(package("test-group/first_fake_package", "1.10.2-r1"))
567         PACKAGES[1].emerge_time = 120
568
569         main("current", None)
570
571         print(red_start + "\nCurrent emerge with incomplete emerge log" +
572               "(causes error in some cases)\n" + color_stop)
573
574         PACKAGES = []
575         PACKAGES.append(package("test-group/third_fake_package", "2.9-r2"))
576
577         main("current", None)
578
579         print(red_start + "\n" + '*'*20 + "\n" + "SIMULATION FINISHED\n" +
580               '*'*20 + color_stop)
581
582
583     else:
584         main(mode, input_packages)