]> git.itanic.dy.fi Git - emerge-timer/blob - emerge-timer.py
Improve current process handling
[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.sh" in i) and ("/bin/bash" not in i)):
251              pack = i.partition('[')[2].partition(']')[0]
252
253              version = pack.partition('/')[2].partition('-')[2]
254
255              while not version[0].isdigit():
256                  version = version.partition('-')[2]
257
258              package_name = pack[:-len(version)-1]
259
260              packages.append([package_name, '-'+version, 12*3600])
261
262
263     for line in f:
264         if ((">>>" in line) and ("emerge" in line)):
265             for p in packages:
266                 if (p[0]+p[1] in line):
267
268                     time = float(line.partition(' ')[0].strip(":"))
269
270                     timestamp = datetime.datetime.fromtimestamp(time)
271                     difference = (now - timestamp).total_seconds()
272
273                     if difference < p[2]:
274                         p[2] = difference
275
276
277     if len(packages) == 0:
278         print "No current emerge process found."
279         return
280
281     print_current_emerges(f, packages)
282
283
284
285
286 def print_current_emerges(f, packages):
287     """Print the current packages that are being merged with the
288     current emerge time."""
289
290
291     print("Currently emerging: ")
292
293     for p in packages:
294         print("\t" + green_start + p[0] + p[1] + color_stop),
295         print("\n\t  current emerge time:\t"),
296
297         organize_times(p[2])
298         print
299
300         average_time = main_loop(f, p[0], True)
301
302         if average_time != 0:
303             print("\n\t  " + '-'*45 + "\n\t  time to finish: \t"),
304
305             if (average_time - p[2]) < 0:
306                 print(green_start + "Any time now" + color_stop),
307
308             else:
309                 organize_times(average_time - p[2])
310
311             print "\n"
312
313
314
315 def list_pretended(f):
316     """Print the average times of pretended packages"""
317
318     packages = []
319     for line in sys.stdin:
320         if "[ebuild" in line:
321             full_name = line.partition('] ')[2].partition(' ')[0]
322
323             version = full_name.partition('/')[2].partition('-')[2]
324             while not version[0].isdigit():
325                 version = version.partition('-')[2]
326
327             package_name = full_name[:-len(version)-1]
328
329             packages.append((package_name, '-' + version))
330
331
332     if len(packages) == 0:
333         return
334
335     print "This is how long these packages would take to emerge"
336
337     all_time = 0
338     for pack in packages:
339
340         print('\t' + green_start + pack[0] + pack[1] + color_stop)
341
342         all_time += main_loop(f, pack[0], True)
343
344         print "\n"
345
346
347     if len(packages) > 1:
348         print("Total emerge time of " + green_start + str(len(packages)) +
349               color_stop + " packages:"),
350         organize_times(all_time)
351
352
353
354
355 def main_loop(f, package, silent):
356     """The main loop which parses the LOGFILE and if needed prints out emerge times."""
357
358     f.seek(0) # Seek to the beginning of the file
359     times = []
360
361     # MAIN LOOP
362     for line in f:
363         if ((">>>" in line) and ("emerge" in line)):
364             if package in line:
365                 version = line.partition(package)[2].partition(' ')[0]
366
367                 if version.strip('-')[0].isdigit():
368                     full_package = package + version
369
370                     time_string = line.partition(">>>")
371                     start_time = float(time_string[0].strip().strip(':'))
372
373
374         elif ((":::" in line) and ("completed emerge" in line)):
375             if package in line:
376                 if version.strip('-')[0].isdigit():
377
378                     time_string = line.partition(":::")
379                     end_time = float(time_string[0].strip().strip(':'))
380
381
382                     emerge_time = end_time - start_time
383
384                     date = get_date(start_time)
385
386
387                     if silent == False:
388                         print(str(len(times)+1) + ". " +
389                               green_start + full_package + color_stop +
390                               "  >>>  " + date + "  >>>  "),
391
392                         organize_times(emerge_time)
393
394                         print("\n" + '-'*90)
395
396
397                     times.append((emerge_time, date))
398
399
400     average_time = print_times(package, times, silent)
401     return average_time
402
403
404
405 def main(status):
406     """Change between current emerge stage and normal operating stage."""
407
408     f = open_log()
409
410     if status == 'current':
411         list_emerge_processes(f)
412         return
413     elif status == 'pretended':
414         list_pretended(f)
415         return
416     else:
417         pass
418
419     package = get_package(package_name)
420
421     print('-'*90)
422
423     main_loop(f, package, False)
424
425     f.close()
426
427
428
429 if __name__ == "__main__":
430
431     if len(sys.argv) == 1:
432         main('current')
433         sys.exit(1)
434
435     elif sys.argv[1] == "-p":
436         main('pretended')
437         sys.exit(1)
438
439     elif ((sys.argv[1] == "-h") or (sys.argv[1] == "--help")):
440         print("Usage: emerge-timer.py [options] [package]\n\nOptions:\n"
441               + green_start + "\t-p" + color_stop +
442               "\tcalculate compile time from piped 'emerge -p' output\n" +
443               green_start + "\t[none]" + color_stop +
444               "\tShow average emerge times for currently compiling packages.")
445         sys.exit(1)
446
447     if len(sys.argv) > 1:
448         package_name = sys.argv[1]
449         main(0)
450
451