Update to genscripts rev 382. This has more fixes for py3k and the modular rewrite...
[gentoolkit.git] / pym / gentoolkit / analyse / rebuild.py
1 #!/usr/bin/python
2 #
3 # Copyright 2010 Brian Dolbec <brian.dolbec@gmail.com>
4 # Copyright(c) 2010, Gentoo Foundation
5 # Distributed under the terms of the GNU General Public License v2
6 #
7
8
9 """Provides a rebuild file of USE flags or keywords used and by
10 what packages according to the Installed package database"""
11
12
13 from __future__ import print_function
14
15
16 import sys
17
18 import gentoolkit
19 from gentoolkit.dbapi import PORTDB, VARDB
20 from gentoolkit.analyse.base import ModuleBase
21 from gentoolkit import pprinter as pp
22 from gentoolkit.analyse.lib import (get_installed_use, get_flags,
23         abs_flag, abs_list, FlagAnalyzer)
24 from gentoolkit.analyse.output import RebuildPrinter
25
26 import portage
27 from portage import os
28
29
30 def cpv_all_diff_use(
31                 cpvs=None,
32                 system_flags=None,
33                 #  override-able for testing
34                 _get_flags=get_flags,
35                 _get_used=get_installed_use
36                 ):
37         """Data gathering and analysis function determines
38         the difference between the current default USE flag settings
39         and the currently installed pkgs recorded USE flag settings
40
41         @type cpvs: list
42         @param cpvs: optional list of [cat/pkg-ver,...] to analyse or
43                         defaults to entire installed pkg db
44         @type: system_flags: list
45         @param system_flags: the current default USE flags as defined
46                         by portage.settings["USE"].split()
47         @type _get_flags: function
48         @param _get_flags: ovride-able for testing,
49                         defaults to gentoolkit.analyse.lib.get_flags
50         @param _get_used: ovride-able for testing,
51                         defaults to gentoolkit.analyse.lib.get_installed_use
52         @rtype dict. {cpv:['flag1', '-flag2',...]}
53         """
54         if cpvs is None:
55                 cpvs = VARDB.cpv_all()
56         cpvs.sort()
57         data = {}
58         # pass them in to override for tests
59         flags = FlagAnalyzer(system_flags,
60                 filter_defaults=True,
61                 target="USE",
62                 _get_flags=_get_flags,
63                 _get_used=get_installed_use
64         )
65         for cpv in cpvs:
66                 plus, minus, unset = flags.analyse_cpv(cpv)
67                 for flag in minus:
68                         plus.add("-"+flag)
69                 if len(plus):
70                         data[cpv] = list(plus)
71         return data
72
73
74 class Rebuild(ModuleBase):
75         """Installed db analysis tool to query the installed databse
76         and produce/output stats for USE flags or keywords/mask.
77         The 'rebuild' action output is in the form suitable for file type output
78         to create a new package.use, package.keywords, package.unmask
79         type files in the event of needing to rebuild the
80         /etc/portage/* user configs
81         """
82         def __init__(self):
83                 ModuleBase.__init__(self)
84                 self.module_name = "rebuild"
85                 self.options = {
86                         "use": False,
87                         "keywords": False,
88                         "unmask": False,
89                         "verbose": False,
90                         "quiet": False,
91                         "exact": False,
92                         "pretend": False,
93                         #"unset": False
94                 }
95                 self.module_opts = {
96                         "-p": ("pretend", "boolean", True),
97                         "--pretend": ("pretend", "boolean", True),
98                         "-e": ("exact", "boolean", True),
99                         "--exact": ("exact", "boolean", True),
100                         "-v": ("verbose", "boolean", True),
101                         "--verbose": ("verbose", "boolean", True),
102                 }
103                 self.formatted_options = [
104                         ("    -h, --help",  "Outputs this useage message"),
105                         ("    -p, --pretend", "Does not actually create the files."),
106                         ("    ", "It directs the outputs to the screen"),
107                         ("    -e, --exact", "will atomize the package with a"),
108                         ("  ", "leading '=' and include the version")
109                 ]
110                 self.formatted_args = [
111                         ("    use",
112                         "causes the action to analyse the installed packages USE flags"),
113                         ("    keywords",
114                         "causes the action to analyse the installed packages keywords"),
115                         ("    unmask",
116                         "causes the action to analyse the installed packages " + \
117                         "current mask status")
118                 ]
119                 self.short_opts = "hepv"
120                 self.long_opts = ("help", "exact", "pretend", "verbose")
121                 self.need_queries = True
122                 self.arg_spec = "TargetSpec"
123                 self.arg_options = ['use', 'keywords', 'unmask']
124                 self.arg_option = False
125                 self.warning = (
126                         "     CAUTION",
127                         "This is beta software and some features/options are incomplete,",
128                         "some features may change in future releases includig its name.",
129                         "The file generated is saved in your home directory",
130                         "Feedback will be appreciated, http://bugs.gentoo.org")
131
132
133
134         def run(self, input_args, quiet=False):
135                 """runs the module
136
137                 @param input_args: input arguments to be parsed
138                 """
139                 self.options['quiet'] = quiet
140                 query = self.main_setup(input_args)
141                 query = self.validate_query(query)
142                 if query in ["use"]:
143                         self.rebuild_use()
144                 elif query in ["keywords"]:
145                         self.rebuild_keywords()
146                 elif query in ["unmask"]:
147                         self.rebuild_unmask()
148
149
150         def rebuild_use(self):
151                 if not self.options["quiet"]:
152                         print()
153                         print("  -- Scanning installed packages for USE flag settings that")
154                         print("     do not match the default settings")
155                 system_use = portage.settings["USE"].split()
156                 output = RebuildPrinter("use", self.options["pretend"], self.options["exact"])
157                 pkgs = cpv_all_diff_use(system_flags=system_use)
158                 pkg_count = len(pkgs)
159                 if self.options["verbose"]:
160                         print()
161                         print((pp.emph("  -- Found ") +  pp.number(str(pkg_count)) +
162                                 pp.emph(" packages that need entries")))
163                         #print pp.emph("     package.use to maintain their current setting")
164                 if pkgs:
165                         pkg_keys = sorted(pkgs)
166                         #print len(pkgs)
167                         if self.options["pretend"] and not self.options["quiet"]:
168                                 print()
169                                 print(pp.globaloption(
170                                         "  -- These are the installed packages & flags " +
171                                         "that were detected"))
172                                 print(pp.globaloption("     to need flag settings other " +
173                                         "than the defaults."))
174                                 print()
175                         elif not self.options["quiet"]:
176                                 print("  -- preparing pkgs for file entries")
177                         flag_count = 0
178                         unique_flags = set()
179                         for pkg in pkg_keys:
180                                 if self.options['verbose']:
181                                         flag_count += len(pkgs[pkg])
182                                         unique_flags.update(abs_list(pkgs[pkg]))
183                                 output(pkg, pkgs[pkg])
184                         if self.options['verbose']:
185                                 message = (pp.emph("     ") +
186                                         pp.number(str(len(unique_flags))) +
187                                         pp.emph(" unique flags\n") + "     " +
188                                         pp.number(str(flag_count))+
189                                         pp.emph(" flag entries\n") + "     " +
190                                         pp.number(str(pkg_count)) +
191                                         pp.emph(" different packages"))
192                                 print()
193                                 print(pp.globaloption("  -- Totals"))
194                                 print(message)
195                                 #print
196                                 #unique = list(unique_flags)
197                                 #unique.sort()
198                                 #print unique
199                         if not self.options["pretend"]:
200                                 filepath = os.path.expanduser('~/package.use.test')
201                                 self.save_file(filepath, output.use_lines)
202
203         def rebuild_keywords(self):
204                 print("Module action not yet available")
205                 print()
206
207         def rebuild_unmask(self):
208                 print("Module action not yet available")
209                 print()
210
211
212         def save_file(self, filepath, data):
213                 """Writes the data to the file determined by filepath
214
215                 @param filepath: string. eg. '/path/to/filename'
216                 @param data: list of lines to write to filepath
217                 """
218                 if  not self.options["quiet"]:
219                         print('   - Saving file: %s' %filepath)
220                 with open(filepath, "w") as output:
221                         output.write('\n'.join(data))
222                 print("   - Done")
223
224
225 def main(input_args):
226         """Common starting method by the analyse master
227         unless all modules are converted to this class method.
228
229         @param input_args: input args as supplied by equery master module.
230         """
231         query_module = Rebuild()
232         query_module.run(input_args, gentoolkit.CONFIG['quiet'])
233
234 # vim: set ts=4 sw=4 tw=79:
235