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