1 |
--- |
2 |
pym/portage/emaint/main.py | 6 +- |
3 |
pym/portage/emaint/modules/merges/__init__.py | 19 ++++ |
4 |
pym/portage/emaint/modules/merges/merges.py | 121 ++++++++++++++++++++++++++ |
5 |
3 files changed, 144 insertions(+), 2 deletions(-) |
6 |
create mode 100644 pym/portage/emaint/modules/merges/__init__.py |
7 |
create mode 100644 pym/portage/emaint/modules/merges/merges.py |
8 |
|
9 |
diff --git a/pym/portage/emaint/main.py b/pym/portage/emaint/main.py |
10 |
index a03ffd7..c1e0c0b 100644 |
11 |
--- a/pym/portage/emaint/main.py |
12 |
+++ b/pym/portage/emaint/main.py |
13 |
@@ -95,10 +95,11 @@ def module_opts(module_controller, module): |
14 |
class TaskHandler(object): |
15 |
"""Handles the running of the tasks it is given""" |
16 |
|
17 |
- def __init__(self, show_progress_bar=True, verbose=True, callback=None): |
18 |
+ def __init__(self, show_progress_bar=True, verbose=True, callback=None, module_output=None): |
19 |
self.show_progress_bar = show_progress_bar |
20 |
self.verbose = verbose |
21 |
self.callback = callback |
22 |
+ self.module_output = module_output |
23 |
self.isatty = os.environ.get('TERM') != 'dumb' and sys.stdout.isatty() |
24 |
self.progress_bar = ProgressBar(self.isatty, title="Emaint", max_desc_length=27) |
25 |
|
26 |
@@ -121,6 +122,7 @@ class TaskHandler(object): |
27 |
onProgress = None |
28 |
kwargs = { |
29 |
'onProgress': onProgress, |
30 |
+ 'module_output': self.module_output, |
31 |
# pass in a copy of the options so a module can not pollute or change |
32 |
# them for other tasks if there is more to do. |
33 |
'options': options.copy() |
34 |
@@ -215,5 +217,5 @@ def emaint_main(myargv): |
35 |
# need to pass the parser options dict to the modules |
36 |
# so they are available if needed. |
37 |
task_opts = options.__dict__ |
38 |
- taskmaster = TaskHandler(callback=print_results) |
39 |
+ taskmaster = TaskHandler(callback=print_results, module_output=sys.stdout) |
40 |
taskmaster.run_tasks(tasks, func, status, options=task_opts) |
41 |
diff --git a/pym/portage/emaint/modules/merges/__init__.py b/pym/portage/emaint/modules/merges/__init__.py |
42 |
new file mode 100644 |
43 |
index 0000000..638d3da |
44 |
--- /dev/null |
45 |
+++ b/pym/portage/emaint/modules/merges/__init__.py |
46 |
@@ -0,0 +1,19 @@ |
47 |
+# Copyright 2005-2014 Gentoo Foundation |
48 |
+# Distributed under the terms of the GNU General Public License v2 |
49 |
+ |
50 |
+"""Scan for failed merges and fix them.""" |
51 |
+ |
52 |
+ |
53 |
+module_spec = { |
54 |
+ 'name': 'merges', |
55 |
+ 'description': __doc__, |
56 |
+ 'provides':{ |
57 |
+ 'merges': { |
58 |
+ 'name': "merges", |
59 |
+ 'class': "MergesHandler", |
60 |
+ 'description': __doc__, |
61 |
+ 'functions': ['check', 'fix',], |
62 |
+ 'func_desc': {} |
63 |
+ } |
64 |
+ } |
65 |
+ } |
66 |
diff --git a/pym/portage/emaint/modules/merges/merges.py b/pym/portage/emaint/modules/merges/merges.py |
67 |
new file mode 100644 |
68 |
index 0000000..f03d9c4 |
69 |
--- /dev/null |
70 |
+++ b/pym/portage/emaint/modules/merges/merges.py |
71 |
@@ -0,0 +1,121 @@ |
72 |
+# Copyright 2005-2014 Gentoo Foundation |
73 |
+# Distributed under the terms of the GNU General Public License v2 |
74 |
+ |
75 |
+import portage |
76 |
+from portage import os, _unicode_encode |
77 |
+from portage.const import PORTAGE_BIN_PATH, PRIVATE_PATH, VDB_PATH |
78 |
+from portage.dep import isvalidatom |
79 |
+ |
80 |
+import shutil |
81 |
+import sys |
82 |
+import subprocess |
83 |
+ |
84 |
+class MergesHandler(object): |
85 |
+ |
86 |
+ short_desc = "Remove failed merges" |
87 |
+ |
88 |
+ @staticmethod |
89 |
+ def name(): |
90 |
+ return "merges" |
91 |
+ |
92 |
+ |
93 |
+ def __init__(self): |
94 |
+ self._eroot = portage.settings['EROOT'] |
95 |
+ self._vardb_path = os.path.join(self._eroot, VDB_PATH) |
96 |
+ |
97 |
+ |
98 |
+ def can_progressbar(self, func): |
99 |
+ if func == 'fix': |
100 |
+ return False |
101 |
+ return True |
102 |
+ |
103 |
+ |
104 |
+ def _failed_packages(self, onProgress=None): |
105 |
+ for cat in os.listdir(self._vardb_path): |
106 |
+ pkgs = os.listdir(os.path.join(self._vardb_path, cat)) |
107 |
+ maxval = len(pkgs) |
108 |
+ for i, pkg in enumerate(pkgs): |
109 |
+ if onProgress: |
110 |
+ onProgress(maxval, i+1) |
111 |
+ |
112 |
+ if '-MERGING-' in pkg: |
113 |
+ yield os.path.join(cat, pkg) |
114 |
+ |
115 |
+ |
116 |
+ def check(self, **kwargs): |
117 |
+ onProgress = kwargs.get('onProgress', None) |
118 |
+ failed_pkgs = set(self._failed_packages(onProgress)) |
119 |
+ errors = ["'%s' failed to merge." % x for x in failed_pkgs] |
120 |
+ return errors |
121 |
+ |
122 |
+ |
123 |
+ def fix(self, **kwargs): |
124 |
+ module_output = kwargs.get('module_output', None) |
125 |
+ failed_pkgs = set(self._failed_packages()) |
126 |
+ if not failed_pkgs: |
127 |
+ return [ 'No failed merges found. ' ] |
128 |
+ |
129 |
+ tracking_path = os.path.join(self._eroot, PRIVATE_PATH, 'failed-merges'); |
130 |
+ try: |
131 |
+ with open(_unicode_encode(tracking_path), 'w') as tracking_file: |
132 |
+ tracking_file.write('\n'.join(failed_pkgs)) |
133 |
+ except IOError as ex: |
134 |
+ errors = [ 'Unable to save failed merges to tracking file: %s\n' |
135 |
+ % str(ex) ] |
136 |
+ errors.append(', '.join(failed_pkgs)) |
137 |
+ |
138 |
+ return errors |
139 |
+ |
140 |
+ pkg_dne = [] |
141 |
+ pkg_atoms = set() |
142 |
+ for failed_pkg in failed_pkgs: |
143 |
+ # validate pkg name |
144 |
+ pkg_name = '%s' % failed_pkg.replace('-MERGING-', '') |
145 |
+ pkg_atom = '=%s' % pkg_name |
146 |
+ if not isvalidatom(pkg_atom): |
147 |
+ pkg_dne.append( "'%s' is an invalid package atom." % pkg_atom) |
148 |
+ if not portage.portdb.cpv_exists(pkg_name): |
149 |
+ pkg_dne.append( "'%s' does not exist in the portage tree." |
150 |
+ % pkg_name) |
151 |
+ pkg_atoms.add(pkg_atom) |
152 |
+ if pkg_dne: |
153 |
+ return pkg_dne |
154 |
+ |
155 |
+ for failed_pkg in failed_pkgs: |
156 |
+ pkg_path = os.path.join(self._vardb_path, failed_pkg) |
157 |
+ # delete failed merge directory |
158 |
+ shutil.rmtree(pkg_path) |
159 |
+ # TODO: try removing package contents to prevent orphaned |
160 |
+ # files |
161 |
+ |
162 |
+ # TODO: rewrite code to use portage's APIs instead of a subprocess |
163 |
+ env = { |
164 |
+ "FEATURES" : "-collision-detect -protect-owned", |
165 |
+ "PATH" : os.environ["PATH"] |
166 |
+ } |
167 |
+ emerge_cmd = ( |
168 |
+ portage._python_interpreter, |
169 |
+ '-b', |
170 |
+ os.path.join(PORTAGE_BIN_PATH, 'emerge'), |
171 |
+ '--quiet', |
172 |
+ '--oneshot', |
173 |
+ '--complete-graph=y' |
174 |
+ ) |
175 |
+ results = [] |
176 |
+ msg = 'Re-Emerging packages that failed to merge...\n' |
177 |
+ if module_output: |
178 |
+ module_output.write(msg) |
179 |
+ else: |
180 |
+ module_output = subprocess.PIPE |
181 |
+ results.append(msg) |
182 |
+ proc = subprocess.Popen(emerge_cmd + tuple(pkg_atoms), env=env, |
183 |
+ stdout=module_output, stderr=sys.stderr) |
184 |
+ output = proc.communicate()[0] |
185 |
+ if output: |
186 |
+ results.append(output) |
187 |
+ if proc.returncode != os.EX_OK: |
188 |
+ finish = "Failed to emerge '%s'" % (' '.join(pkg_atoms)) |
189 |
+ else: |
190 |
+ finish = "Successfully emerged '%s'" % (' '.join(pkg_atoms)) |
191 |
+ results.append(finish) |
192 |
+ return results |
193 |
-- |
194 |
1.8.3.2 |