]> git.itanic.dy.fi Git - emerge-timer/blob - emerge-timer.py
dc91c41c0c068ccab21e78fd7e19bfcf2dd5501b
[emerge-timer] / emerge-timer.py
1 #!/usr/bin/python
2
3
4 import sys, subprocess, datetime, os
5
6
7 PORTDIR = "/usr/portage/"
8 LOGFILE = "/var/log/emerge.log"
9
10
11
12 green_start = "\033[32m"
13 color_stop = "\033[m"
14
15
16
17
18
19 def organize_times(time):
20     """Takes the emerge time in seconds, organizes it into
21     days, hours, minutes or seconds and finally prints that out."""
22
23     days = time/(3600*24)
24     hours = (days - int(days))*24
25     minutes = (hours - int(hours))*60
26     seconds = (minutes - int(minutes))*60
27
28     days = int(days)
29     hours = int(hours)
30     minutes = int(minutes)
31     seconds = int(round(seconds))
32
33     if days > 0:
34         print_days = (green_start + str(days) + color_stop + " day")
35         if days != 1:
36             print_days += "s"
37         print print_days,
38
39     if hours > 0:
40         print_hours = (green_start + str(hours) + color_stop + " hour")
41         if hours != 1:
42             print_hours += "s"
43         print print_hours,
44
45     if minutes > 0:
46         print_minutes = (green_start + str(minutes) + color_stop + " minute")
47         if minutes != 1:
48             print_minutes += "s"
49         print print_minutes,
50
51     printable_sec = (green_start + str(seconds) + color_stop + " second")
52     if seconds != 1:
53         printable_sec += "s"
54     print printable_sec,
55
56
57
58 def get_date(emerge_start):
59     """Take the emerge startup time in seconds and turn it into a
60     correct date."""
61
62     date = datetime.datetime.fromtimestamp(emerge_start)
63
64     year = str(date.year)
65     month = str(date.month)
66     day = str(date.day)
67     weekday = date.weekday()
68
69     if weekday == 0: weekday = 'Mon '
70     if weekday == 1: weekday = 'Tue '
71     if weekday == 2: weekday = 'Wed '
72     if weekday == 3: weekday = 'Thu '
73     if weekday == 4: weekday = 'Fri '
74     if weekday == 5: weekday = 'Sat '
75     if weekday == 6: weekday = 'Sun '
76
77     hour = str(date.hour)
78     minute = str(date.minute)
79     second = str(date.second)
80
81     # This is in the format 'Mon 23.05.2011 00:20:14'
82
83     date = weekday + "{:%d.%m.%Y %H:%M:%S}".format(date)
84
85     return date
86
87
88
89 def list_all_packages():
90     """Go through PORTDIR and create a list of all the packages in portage"""
91
92     root = os.listdir(PORTDIR)
93     all_packages = []
94
95     for package_group in root:
96         group_dir = PORTDIR + package_group
97         if (os.path.isdir(group_dir)
98             and (package_group != "licenses")
99             and (package_group != "metadata")):
100
101             name_dir = os.listdir(group_dir)
102
103             for package_name in name_dir:
104                 if ".xml" not in package_name:
105
106                     all_packages.append((package_group +
107                                          '/' + package_name))
108
109     return all_packages
110
111
112
113
114 def get_package(name):
115     """Take the user-input package name and search for it
116     in PORTDIR. """
117
118     dirlist = os.listdir(PORTDIR)
119     possible_package = []
120
121
122     # If the given name is in the format xxx/zzz
123     # assume that xxx is the package group
124     if '/' in name:
125         group = name.partition('/')[0]
126         pkg = name.partition('/')[2]
127         directory = PORTDIR + group
128
129         if group in dirlist:
130             dirs = os.listdir(directory)
131             if pkg in dirs:
132                 possible_package.append(name)
133
134
135     # Go through the directory listing searching for anything
136     # that matches the given name
137     for i in dirlist:
138         directory = PORTDIR + i
139         if os.path.isdir(directory):
140             dirs = os.listdir(directory)
141             if name in dirs:
142                 possible_package.append(i + '/' + name)
143
144
145     if len(possible_package) > 1:
146         print("Multiple packages found for '" + name + "'.")
147         print("Possible packages: ")
148         for value in possible_package:
149             print("\t" + value)
150
151
152     elif len(possible_package) == 1:
153         package = possible_package[0]
154         return package
155
156
157     else:
158         print("No package '" + name + "' found")
159
160
161     sys.exit(1)
162
163
164
165 def print_times(package, times, silent):
166     """Print the maximum/minimum/average times of the given emerge package.
167     If we're in the 'current emerge stage' (the 'silent' flag is True)
168     print the appropriate comment for that package."""
169
170
171     times.sort()
172     times.reverse()
173
174
175     # This should be True if we're in current emerge stage
176     if silent == True:
177         if len(times) == 0:
178             print("\t  no previous emerges found for this package"),
179             return 0
180
181         elif len(times) == 1:
182             print("\t  previous emerge time:\t"),
183             organize_times(times[0][0])
184             print("(only one emerge previously)"),
185             return times[0][0]
186
187         else:
188             print("\t  average emerge time:\t"),
189             all_times = 0
190             for i in times:
191                 all_times += i[0]
192
193             organize_times(all_times/len(times))
194
195         return all_times/len(times)
196
197
198     if len(times) == 1:
199         print(green_start + package + color_stop + " emerged once")
200
201     elif len(times) > 1:
202         print(green_start + package + color_stop + " emerged " + green_start +
203               str(len(times)) + color_stop + " times\n")
204
205         print "Max time:\t",
206         organize_times(times[0][0])
207         print "at", times[0][1]
208
209         print "Min time:\t",
210         organize_times(times[len(times)-1][0])
211         print "at", times[len(times)-1][1]
212
213         all_times = 0
214         for i in times:
215             all_times += i[0]
216
217         print "Average time\t",
218         organize_times(all_times/len(times))
219         print
220
221         print "In total spent\t",
222         organize_times(all_times)
223         print("emerging " + green_start +
224               package + color_stop)
225
226
227
228 def open_log():
229     """Attempt to open the LOGFILE."""
230
231     try:
232         f = open(LOGFILE, 'r')
233     except IOError as detail:
234         print detail
235         sys.exit(1)
236     finally:
237         return f
238
239
240
241 def list_emerge_processes(f):
242     """Look for the ebuild process with ps. If the process is found parse
243     the command for the package. With this package search the LOGFILE for
244     the emerge startup time."""
245
246     now = datetime.datetime.today()
247     packages = []
248
249     for i in os.popen("ps ax"):
250         if (("ebuild" in i) and ("/bin/bash" not in i)):
251              pack = i.partition('[')[2].partition(']')[0]
252
253              packages.append([pack, 12*3600])
254
255
256     for line in f:
257         if ((">>>" in line) and ("emerge" in line)):
258             for p in packages:
259                 if (p[0] in line):
260
261                     time = float(line.partition(' ')[0].strip(":"))
262
263                     timestamp = datetime.datetime.fromtimestamp(time)
264                     difference = (now - timestamp).total_seconds()
265
266                     if difference < p[1]:
267                         p[1] = difference
268
269
270     if len(packages) == 0:
271         print "No current emerge process found."
272         return
273
274     print_current_emerges(f, packages)
275
276
277
278
279 def print_current_emerges(f, packages):
280     """Print the current packages that are being merged with the
281     current emerge time."""
282
283     all_packages = list_all_packages()
284
285     print("Currently emerging: ")
286
287     for p in packages:
288         print("\t" + green_start + p[0] + color_stop),
289         print("\n\t  current emerge time:\t"),
290
291         organize_times(p[1])
292         print
293
294
295         for i in all_packages:
296             if i in p[0]:
297                 average_time = main_loop(f, i, True)
298
299         if average_time != 0:
300             print("\n\t  " + '-'*45 + "\n\t  time to finish: \t"),
301             if (average_time - p[1]) < 0:
302                 print(green_start + "Any time now" + color_stop),
303             else:
304                 organize_times(average_time - p[1])
305             print "\n"
306
307
308
309 def list_pretended(f):
310     """Print the average times of pretended packages"""
311
312     all_packages = list_all_packages()
313
314     packages = []
315     for line in sys.stdin:
316         if "[ebuild" in line:
317             full_name = line.partition('] ')[2].partition(' ')[0]
318
319             for i in all_packages:
320                 if i in full_name:
321                     packages.append((i, full_name[len(i):]))
322
323     if len(packages) == 0:
324         return
325
326     print "This is how long these packages would take to emerge"
327
328     all_time = 0
329     for pack in packages:
330
331         print('\t' + green_start + pack[0] + pack[1] + color_stop)
332
333         all_time += main_loop(f, pack[0], True)
334
335         print "\n"
336
337
338     if all_time != 0:
339         print("Total emerge time of " + green_start + str(len(packages)) +
340               color_stop + " packages:"),
341         organize_times(all_time)
342
343
344
345
346 def main_loop(f, package, silent):
347     """The main loop which parses the LOGFILE and if needed prints out emerge times."""
348
349     f.seek(0) # Seek to the beginning of the file
350     times = []
351
352     # MAIN LOOP
353     for line in f:
354         if package in line:
355             st = line.split(' ')
356
357             for string in st:
358                 if ((package in string) and (len(string) > len(package)+1)
359                     and (string[len(package)+1].isdigit())):
360                     full_package = st[st.index(string)]
361
362
363                     if ((">>>" in line) and ("emerge" in line)):
364                         time_string = line.partition(">>>")
365                         time = float(time_string[0].strip().strip(":"))
366
367                     try:
368                         if (":::" in line) and ("completed emerge" in line):
369                             time_string = line.partition(":::")
370                             time2 = float(time_string[0].strip().strip(":"))
371
372
373                             emerge_time = time2-time
374
375                             date = get_date(time)
376
377
378                             if silent == False:
379                                 print(str(len(times)+1) + "."),
380                                 print(green_start + full_package + color_stop + "  >>>  "
381                                       + date + "  >>> "),
382
383                                 organize_times(emerge_time)
384
385                                 print("\n" + '-'*90)
386
387                             times.append((emerge_time, date))
388                     except IndexError:
389                         pass
390
391     average_time = print_times(package, times, silent)
392     return average_time
393
394
395
396 def main(status):
397     """Change between current emerge stage and normal operating stage."""
398
399     f = open_log()
400
401     if status == 'current':
402         list_emerge_processes(f)
403         return
404     elif status == 'pretended':
405         list_pretended(f)
406         return
407     else:
408         pass
409
410     package = get_package(package_name)
411
412     print('-'*90)
413
414     main_loop(f, package, False)
415
416     f.close()
417
418
419
420 if __name__ == "__main__":
421
422     if len(sys.argv) == 1:
423         main('current')
424         sys.exit(1)
425
426     elif sys.argv[1] == "-p":
427         main('pretended')
428         sys.exit(1)
429
430     elif ((sys.argv[1] == "-h") or (sys.argv[1] == "--help")):
431         print("Usage: emerge-timer.py [options] [package]\n\nOptions:\n"
432               + green_start + "\t-p" + color_stop +
433               "\tcalculate compile time from piped 'emerge -p' output\n" +
434               green_start + "\t[none]" + color_stop +
435               "\tShow average emerge times for currently compiling packages.")
436         sys.exit(1)
437
438     if len(sys.argv) > 1:
439         package_name = sys.argv[1]
440         main(0)
441
442