Merge branch 'gentoolkit' of git+ssh://git.overlays.gentoo.org/proj/gentoolkit into...
[gentoolkit.git] / pym / gentoolkit / revdep_rebuild / rebuild.py
1 #!/usr/bin/python
2 # -*- coding: utf-8 -*-
3
4
5 """ Rebuild module
6
7 Main program, cli parsing and api program control and operation
8
9 Author: SÅ‚awomir Lis <lis.slawek@gmail.com>
10         revdep-rebuild original author: Stanislav Brabec
11         revdep-rebuild original rewrite Author: Michael A. Smith
12 Current Maintainer: Paul Varner <fuzzyray@gentoo.org>
13 Creation date: 2010/10/17
14 License: BSD
15 """
16
17 from __future__ import print_function
18
19 import os
20 import sys
21 import getopt
22 import logging
23 from portage.output import bold, red, blue, yellow, green, nocolor
24
25 from .analyse import analyse
26 from .stuff import get_masking_status
27 from .cache import check_temp_files, read_cache
28 from .assign import get_slotted_cps
29 from .settings import DEFAULTS
30 from . import __version__
31
32
33 APP_NAME = sys.argv[0]
34 VERSION = __version__
35
36 __productname__ = "revdep-ng"
37
38
39 # functions
40
41 def print_usage():
42         """Outputs the help message"""
43         print( APP_NAME + ': (' + VERSION +')')
44         print()
45         print('This is free software; see the source for copying conditions.')
46         print()
47         print('Usage: ' + APP_NAME + ' [OPTIONS] [--] [EMERGE_OPTIONS]')
48         print()
49         print('Broken reverse dependency rebuilder, python implementation.')
50         print()
51         print('Available options:')
52         print('''
53   -C, --nocolor         Turn off colored output
54   -d, --debug           Print debug informations
55   -e, --exact           Emerge based on exact package version
56   -h, --help            Print this usage
57   -i, --ignore          Ignore temporary files from previous runs
58                         (also won't create any)
59   -L, --library NAME    Unconditionally emerge existing packages that use
60       --library=NAME    the library with NAME. NAME can be a full or partial
61                         library name
62   -l, --no-ld-path      Do not set LD_LIBRARY_PATH
63   -o, --no-order        Do not check the build order
64                         (Saves time, but may cause breakage.)
65   -p, --pretend         Do a trial run without actually emerging anything
66                         (also passed to emerge command)
67   -q, --quiet           Be less verbose (also passed to emerge command)
68   -v, --verbose         Be more verbose (also passed to emerge command)
69 ''')
70         print( 'Calls emerge, options after -- are ignored by ' + APP_NAME)
71         print('and passed directly to emerge.')
72
73
74 def init_logger(settings):
75         """Creates and iitializes our logger according to the settings"""
76         logger = logging.getLogger()
77         log_handler = logging.StreamHandler()
78         log_fmt = logging.Formatter('%(msg)s')
79         log_handler.setFormatter(log_fmt)
80         logger.addHandler(log_handler)
81         if settings['quiet']:
82                 logger.setLevel(logging.ERROR)
83         elif settings['VERBOSITY'] == 2:
84                 logger.setLevel(logging.INFO)
85         elif settings['debug']:
86                 logger.setLevel(logging.DEBUG)
87         else:
88                 logger.setLevel(logging.WARNING)
89         return logger
90
91
92 def parse_options():
93         """Parses the command line options an sets settings accordingly"""
94
95         # TODO: Verify: options: no-ld-path, no-order, no-progress
96         #are not appliable
97
98         settings = DEFAULTS.copy()
99         try:
100                 opts, args = getopt.getopt(sys.argv[1:], 
101                         'dehiklopqvCL:P', 
102                         ['nocolor', 'debug', 'exact', 'help', 'ignore',
103                         'keep-temp', 'library=', 'no-ld-path', 'no-order',
104                         'pretend', 'no-pretend', 'no-progress', 'quiet', 'verbose'])
105
106                 do_help = False
107                 for key, val in opts:
108                         if key in ('-h', '--help'):
109                                 do_help = True
110                         elif key in ('-q', '--quiet'):
111                                 settings['quiet'] = True
112                                 settings['VERBOSITY'] = 0
113                         elif key in ('-v', '--verbose'):
114                                 settings['VERBOSITY'] = 2
115                         elif key in ('-d', '--debug'):
116                                 settings['debug'] = True
117                                 settings['VERBOSITY'] = 3
118                         elif key in ('-p', '--pretend'):
119                                 settings['PRETEND'] = True
120                         elif key == '--no-pretend':
121                                 settings['NO_PRETEND'] = True
122                         elif key in ('-e', '--exact'):
123                                 settings['EXACT'] = True
124                         elif key in ('-C', '--nocolor', '--no-color'):
125                                 settings['nocolor'] = True
126                         elif key in ('-L', '--library', '--library='):
127                                 settings['library'] = settings['library'].union(val.split(','))
128                         elif key in ('-i', '--ignore'):
129                                 settings['USE_TMP_FILES'] = False
130
131                 settings['pass_through_options'] = " " + " ".join(args)
132         except getopt.GetoptError:
133                 #logging.info(red('Unrecognized option\n'))
134                 print(red('Unrecognized option\n'))
135                 print_usage()
136                 sys.exit(2)
137         if do_help:
138                 print_usage()
139                 sys.exit(0)
140         return settings
141
142
143 def rebuild(logger, assigned, settings):
144         """rebuilds the assigned pkgs"""
145         
146         args = settings['pass_through_options']
147         if settings['EXACT']:
148                 emerge_command = '=' + ' ='.join(assigned)
149         else:
150                 emerge_command = ' '.join(get_slotted_cps(assigned, logger))
151         if settings['PRETEND']:
152                 args += ' --pretend'
153         if settings['VERBOSITY'] >= 2:
154                 args += ' --verbose'
155         elif settings['VERBOSITY'] < 1:
156                 args += ' --quiet'
157         if settings['nocolor']:
158                 args += ' --color n'
159
160         if len(emerge_command) == 0:
161                 logger.warn(bold('\nThere is nothing to emerge. Exiting.'))
162                 return 0
163
164         emerge_command = emerge_command
165
166         logger.warn(yellow(
167                 '\nemerge') + args + 
168                 ' --oneshot --complete-graph=y ' +
169                 bold(emerge_command))
170         
171         success = os.system(
172                 'emerge ' + args + 
173                 ' --oneshot --complete-graph=y ' + 
174                 emerge_command)
175         return success
176
177
178 def main(settings=None, logger=None):
179         """Main program operation method....
180         
181         @param settings: dict.  defaults to settings.DEFAULTS
182         @param logger: python logging module defaults to init_logger(settings)
183         @return boolean  success/failure
184         """
185
186         if settings is None:
187                 print("NO Input settings, using defaults...")
188                 settings = DEFAULTS.copy()
189
190         if logger is None:
191                 logger = init_logger(settings)
192
193         _libs_to_check = settings['library']
194
195         if not settings['stdout'].isatty() or settings['nocolor']:
196                 nocolor()
197
198         #TODO: Development warning
199         logger.warn(blue(' * ') + 
200                 yellow('This is a development version, '
201                         'so it may not work correctly'))
202         logger.warn(blue(' * ') + 
203                 yellow('The original revdep-rebuild script is '
204                         'installed as revdep-rebuild.sh'))
205
206         if os.getuid() != 0 and not settings['PRETEND']:
207                 logger.warn(blue(' * ') + 
208                         yellow('You are not root, adding --pretend to portage options'))
209                 settings['PRETEND'] = True
210
211         if settings['library']:
212                 logger.warn(green(' * ') + 
213                         "Looking for libraries: %s" % (bold(', '.join(settings['library']))))
214
215         if settings['USE_TMP_FILES'] \
216                         and check_temp_files(settings['DEFAULT_TMP_DIR'], logger=logger):
217                 libraries, la_libraries, libraries_links, binaries = read_cache(
218                         settings['DEFAULT_TMP_DIR'])
219                 assigned = analyse(
220                         settings=settings,
221                         logger=logger,
222                         libraries=libraries,
223                         la_libraries=la_libraries, 
224                         libraries_links=libraries_links,
225                         binaries=binaries,
226                         _libs_to_check=_libs_to_check)
227         else:
228                 assigned = analyse(settings, logger, _libs_to_check=_libs_to_check)
229
230         if not assigned:
231                 logger.warn('\n' + bold('Your system is consistent'))
232                 # return the correct exit code
233                 return 0
234
235         has_masked = False
236         tmp = []
237         for ebuild in assigned:
238                 if get_masking_status(ebuild):
239                         has_masked = True
240                         logger.warn('!!! ' + red('All ebuilds that could satisfy: ') + 
241                                 green(ebuild) + red(' have been masked'))
242                 else:
243                         tmp.append(ebuild)
244         assigned = tmp
245
246         if has_masked:
247                 logger.info(red(' * ') + 
248                         'Unmask all ebuild(s) listed above and call revdep-rebuild '
249                         'again or manually emerge given packages.')
250
251         success = rebuild(logger, assigned, settings)
252         logger.debug("rebuild return code = %i" %success)
253         return success