f74b3dde06d42c052e446caf23578a97b3d35359
[portage.git] / bin / regenworld
1 #!/usr/bin/python
2 # Copyright 1999-2013 Gentoo Foundation
3 # Distributed under the terms of the GNU General Public License v2
4
5 from __future__ import print_function
6
7 import sys
8 from os import path as osp
9 pym_path = osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), "pym")
10 sys.path.insert(0, pym_path)
11 import portage
12 portage._internal_caller = True
13 from portage import os
14 from portage._sets.files import StaticFileSet, WorldSelectedSet
15
16 import re
17 import tempfile
18 import textwrap
19
20 __candidatematcher__ = re.compile("^[0-9]+: \\*\\*\\* emerge ")
21 __noncandidatematcher__ = re.compile(" sync( |$)| clean( |$)| search( |$)|--oneshot|--fetchonly| unmerge( |$)")
22
23 def issyspkg(pkgline):
24         return (pkgline[0] == "*")
25
26 def iscandidate(logline):
27         return (__candidatematcher__.match(logline) \
28                                 and not __noncandidatematcher__.search(logline))
29
30 def getpkginfo(logline):
31         logline = re.sub("^[0-9]+: \\*\\*\\* emerge ", "", logline)
32         logline = logline.strip()
33         logline = re.sub("(\\S+\\.(ebuild|tbz2))|(--\\S+)|inject ", "", logline)
34         return logline.strip()
35
36 __uniqlist__ = []
37 def isunwanted(pkgline):
38         if pkgline in ["world", "system", "depclean", "info", "regen", ""]:
39                 return False
40         elif pkgline in __uniqlist__:
41                 return False
42         elif not re.search("^[a-zA-Z<>=~]", pkgline):
43                 return False
44         else:
45                 __uniqlist__.append(pkgline)
46                 return True
47
48 eroot = portage.settings['EROOT']
49 world_file = os.path.join(eroot, portage.WORLD_FILE)
50
51 # show a little description if we have arguments
52 if len(sys.argv) >= 2 and sys.argv[1] in ["-h", "--help"]:
53         print("This script regenerates the portage world file by checking the portage")
54         print("logfile for all actions that you've done in the past. It ignores any")
55         print("arguments except --help. It is recommended that you make a backup of")
56         print("your existing world file (%s) before using this tool." % world_file)
57         sys.exit(0)
58
59 worldlist = portage.grabfile(world_file)
60 syslist = [x for x in portage.settings.packages if issyspkg(x)]
61
62 logfile = portage.grabfile(os.path.join(eroot, "var/log/emerge.log"))
63 biglist = [getpkginfo(x) for x in logfile if iscandidate(x)]
64 tmplist = []
65 for l in biglist:
66         tmplist += l.split()
67 biglist = [x for x in tmplist if isunwanted(x)]
68 #for p in biglist:
69 #       print(p)
70 #sys.exit(0)
71
72 # resolving virtuals
73 realsyslist = []
74 for mykey in syslist:
75         # drop the asterix
76         mykey = mykey[1:]
77         #print("candidate:",mykey)
78         mylist = portage.db[eroot]["vartree"].dbapi.match(mykey)
79         if mylist:
80                 mykey=portage.cpv_getkey(mylist[0])
81                 if mykey not in realsyslist:
82                         realsyslist.append(mykey)
83
84 for mykey in biglist:
85         #print("checking:",mykey)
86         try:
87                 mylist = portage.db[eroot]["vartree"].dbapi.match(mykey)
88         except (portage.exception.InvalidAtom, KeyError):
89                 if "--debug" in sys.argv:
90                         print("* ignoring broken log entry for %s (likely injected)" % mykey)
91         except ValueError as e:
92                 try:
93                         print("* %s is an ambiguous package name, candidates are:\n%s" % (mykey, e))
94                 except AttributeError:
95                         # FIXME: Find out what causes this (bug #344845).
96                         print("* %s is an ambiguous package name" % (mykey,))
97                 continue
98         if mylist:
99                 #print "mylist:",mylist
100                 myfavkey=portage.cpv_getkey(mylist[0])
101                 if (myfavkey not in realsyslist) and (myfavkey not in worldlist):
102                         print("add to world:",myfavkey)
103                         worldlist.append(myfavkey)
104
105 if not worldlist:
106         pass
107 else:
108         existing_set = WorldSelectedSet(eroot)
109         existing_set.load()
110
111         if not existing_set:
112                 existing_set.replace(worldlist)
113         else:
114                 old_world = existing_set._filename
115                 fd, tmp_filename = tempfile.mkstemp(suffix=".tmp",
116                         prefix=os.path.basename(old_world) + ".",
117                         dir=os.path.dirname(old_world))
118                 os.close(fd)
119
120                 new_set = StaticFileSet(tmp_filename)
121                 new_set.update(worldlist)
122
123                 if existing_set.getAtoms() == new_set.getAtoms():
124                         os.unlink(tmp_filename)
125                 else:
126                         new_set.write()
127
128                         msg = "Please review differences between old and new files, " + \
129                                 "and replace the old file if desired."
130
131                         portage.util.writemsg_stdout("\n",
132                                 noiselevel=-1)
133                         for line in textwrap.wrap(msg, 65):
134                                 portage.util.writemsg_stdout("%s\n" % line,
135                                         noiselevel=-1)
136                         portage.util.writemsg_stdout("\n",
137                                 noiselevel=-1)
138                         portage.util.writemsg_stdout("  old: %s\n\n" % old_world,
139                                 noiselevel=-1)
140                         portage.util.writemsg_stdout("  new: %s\n\n" % tmp_filename,
141                                 noiselevel=-1)