1 |
commit: e2d88ef3ff5958571ef2da5f69e8756522390f0f |
2 |
Author: Zac Medico <zmedico <AT> gentoo <DOT> org> |
3 |
AuthorDate: Fri Jul 1 06:48:03 2016 +0000 |
4 |
Commit: Zac Medico <zmedico <AT> gentoo <DOT> org> |
5 |
CommitDate: Sat Jul 2 21:04:47 2016 +0000 |
6 |
URL: https://gitweb.gentoo.org/proj/portage.git/commit/?id=e2d88ef3 |
7 |
|
8 |
Add emerge --autounmask-continue option (bug 582624) |
9 |
|
10 |
This option will cause emerge to automatically apply autounmask changes |
11 |
to configuration files, and continue to execute the specified command. |
12 |
If the dependency calculation is not entirely successful, then emerge |
13 |
will simply abort without modifying any configuration files. |
14 |
|
15 |
This sort of behavior can be very useful in a continuous integration |
16 |
setting, where the emerge invocation might be inside of a container that |
17 |
is later discarded (so there is no threat of negative consequences). |
18 |
It's also safe for general use, when combined with the --ask option. |
19 |
|
20 |
X-Gentoo-Bug: 582624 |
21 |
X-Gentoo-Bug-url: https://bugs.gentoo.org/show_bug.cgi?id=582624 |
22 |
Acked-by: Brian Dolbec <dolsen <AT> gentoo.org> |
23 |
|
24 |
man/emerge.1 | 14 +++++++++++- |
25 |
pym/_emerge/actions.py | 36 +++++++++++++++++++++++------ |
26 |
pym/_emerge/depgraph.py | 40 +++++++++++++++++++++++++++++---- |
27 |
pym/_emerge/main.py | 9 ++++++++ |
28 |
pym/portage/tests/emerge/test_simple.py | 15 +++++++++++++ |
29 |
5 files changed, 102 insertions(+), 12 deletions(-) |
30 |
|
31 |
diff --git a/man/emerge.1 b/man/emerge.1 |
32 |
index 75862d7..da1d852 100644 |
33 |
--- a/man/emerge.1 |
34 |
+++ b/man/emerge.1 |
35 |
@@ -1,4 +1,4 @@ |
36 |
-.TH "EMERGE" "1" "Feb 2016" "Portage VERSION" "Portage" |
37 |
+.TH "EMERGE" "1" "Jul 2016" "Portage VERSION" "Portage" |
38 |
.SH "NAME" |
39 |
emerge \- Command\-line interface to the Portage system |
40 |
.SH "SYNOPSIS" |
41 |
@@ -361,6 +361,18 @@ the specified configuration file(s), or enable the |
42 |
\fBEMERGE_DEFAULT_OPTS\fR variable may be used to |
43 |
disable this option by default in \fBmake.conf\fR(5). |
44 |
.TP |
45 |
+.BR "\-\-autounmask\-continue [ y | n ]" |
46 |
+Automatically apply autounmask changes to configuration |
47 |
+files, and continue to execute the specified command. If |
48 |
+the dependency calculation is not entirely successful, then |
49 |
+emerge will simply abort without modifying any configuration |
50 |
+files. |
51 |
+\fBWARNING:\fR |
52 |
+This option is intended to be used only with great caution, |
53 |
+since it is possible for it to make nonsensical configuration |
54 |
+changes which may lead to system breakage. Therefore, it is |
55 |
+advisable to use \fB\-\-ask\fR together with this option. |
56 |
+.TP |
57 |
.BR "\-\-autounmask\-only [ y | n ]" |
58 |
Instead of doing any package building, just unmask |
59 |
packages and generate package.use settings as necessary |
60 |
|
61 |
diff --git a/pym/_emerge/actions.py b/pym/_emerge/actions.py |
62 |
index 2ca7902..1dc2b0d 100644 |
63 |
--- a/pym/_emerge/actions.py |
64 |
+++ b/pym/_emerge/actions.py |
65 |
@@ -96,8 +96,22 @@ if sys.hexversion >= 0x3000000: |
66 |
else: |
67 |
_unicode = unicode |
68 |
|
69 |
-def action_build(settings, trees, mtimedb, |
70 |
- myopts, myaction, myfiles, spinner): |
71 |
+def action_build(emerge_config, trees=DeprecationWarning, |
72 |
+ mtimedb=DeprecationWarning, myopts=DeprecationWarning, |
73 |
+ myaction=DeprecationWarning, myfiles=DeprecationWarning, spinner=None): |
74 |
+ |
75 |
+ if not isinstance(emerge_config, _emerge_config): |
76 |
+ warnings.warn("_emerge.actions.action_build() now expects " |
77 |
+ "an _emerge_config instance as the first parameter", |
78 |
+ DeprecationWarning, stacklevel=2) |
79 |
+ emerge_config = load_emerge_config( |
80 |
+ action=myaction, args=myfiles, trees=trees, opts=myopts) |
81 |
+ adjust_configs(emerge_config.opts, emerge_config.trees) |
82 |
+ |
83 |
+ settings, trees, mtimedb = emerge_config |
84 |
+ myopts = emerge_config.opts |
85 |
+ myaction = emerge_config.action |
86 |
+ myfiles = emerge_config.args |
87 |
|
88 |
if '--usepkgonly' not in myopts: |
89 |
old_tree_timestamp_warn(settings['PORTDIR'], settings) |
90 |
@@ -327,6 +341,11 @@ def action_build(settings, trees, mtimedb, |
91 |
display_missing_pkg_set(root_config, e.value) |
92 |
return 1 |
93 |
|
94 |
+ if success and mydepgraph.need_config_reload(): |
95 |
+ load_emerge_config(emerge_config=emerge_config) |
96 |
+ adjust_configs(emerge_config.opts, emerge_config.trees) |
97 |
+ settings, trees, mtimedb = emerge_config |
98 |
+ |
99 |
if "--autounmask-only" in myopts: |
100 |
mydepgraph.display_problems() |
101 |
return 0 |
102 |
@@ -2384,7 +2403,13 @@ def load_emerge_config(emerge_config=None, **kargs): |
103 |
settings = root_trees["vartree"].settings |
104 |
settings._init_dirs() |
105 |
setconfig = load_default_config(settings, root_trees) |
106 |
- root_trees["root_config"] = RootConfig(settings, root_trees, setconfig) |
107 |
+ root_config = RootConfig(settings, root_trees, setconfig) |
108 |
+ if "root_config" in root_trees: |
109 |
+ # Propagate changes to the existing instance, |
110 |
+ # which may be referenced by a depgraph. |
111 |
+ root_trees["root_config"].update(root_config) |
112 |
+ else: |
113 |
+ root_trees["root_config"] = root_config |
114 |
|
115 |
target_eroot = emerge_config.trees._target_eroot |
116 |
emerge_config.target_config = \ |
117 |
@@ -3230,10 +3255,7 @@ def run_action(emerge_config): |
118 |
except OSError: |
119 |
writemsg("Please install eselect to use this feature.\n", |
120 |
noiselevel=-1) |
121 |
- retval = action_build(emerge_config.target_config.settings, |
122 |
- emerge_config.trees, emerge_config.target_config.mtimedb, |
123 |
- emerge_config.opts, emerge_config.action, |
124 |
- emerge_config.args, spinner) |
125 |
+ retval = action_build(emerge_config, spinner=spinner) |
126 |
post_emerge(emerge_config.action, emerge_config.opts, |
127 |
emerge_config.args, emerge_config.target_config.root, |
128 |
emerge_config.trees, emerge_config.target_config.mtimedb, retval) |
129 |
|
130 |
diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py |
131 |
index c1c37b4..fc957f5 100644 |
132 |
--- a/pym/_emerge/depgraph.py |
133 |
+++ b/pym/_emerge/depgraph.py |
134 |
@@ -431,6 +431,7 @@ class _dynamic_depgraph_config(object): |
135 |
self._slot_operator_replace_installed = backtrack_parameters.slot_operator_replace_installed |
136 |
self._prune_rebuilds = backtrack_parameters.prune_rebuilds |
137 |
self._need_restart = False |
138 |
+ self._need_config_reload = False |
139 |
# For conditions that always require user intervention, such as |
140 |
# unsatisfied REQUIRED_USE (currently has no autounmask support). |
141 |
self._skip_restart = False |
142 |
@@ -438,6 +439,7 @@ class _dynamic_depgraph_config(object): |
143 |
|
144 |
self._buildpkgonly_deps_unsatisfied = False |
145 |
self._autounmask = depgraph._frozen_config.myopts.get('--autounmask') != 'n' |
146 |
+ self._displayed_autounmask = False |
147 |
self._success_without_autounmask = False |
148 |
self._required_use_unsatisfied = False |
149 |
self._traverse_ignored_deps = False |
150 |
@@ -4179,11 +4181,30 @@ class depgraph(object): |
151 |
self._dynamic_config._needed_license_changes) : |
152 |
#We failed if the user needs to change the configuration |
153 |
self._dynamic_config._success_without_autounmask = True |
154 |
+ if (self._frozen_config.myopts.get("--autounmask-continue") is True and |
155 |
+ "--pretend" not in self._frozen_config.myopts): |
156 |
+ # This will return false if it fails or if the user |
157 |
+ # aborts via --ask. |
158 |
+ if self._display_autounmask(autounmask_continue=True): |
159 |
+ self._apply_autounmask_continue_state() |
160 |
+ self._dynamic_config._need_config_reload = True |
161 |
+ return True, myfavorites |
162 |
return False, myfavorites |
163 |
|
164 |
# We're true here unless we are missing binaries. |
165 |
return (True, myfavorites) |
166 |
|
167 |
+ def _apply_autounmask_continue_state(self): |
168 |
+ """ |
169 |
+ Apply autounmask changes to Package instances, so that their |
170 |
+ state will be consistent configuration file changes. |
171 |
+ """ |
172 |
+ for node in self._dynamic_config._serialized_tasks_cache: |
173 |
+ if isinstance(node, Package): |
174 |
+ effective_use = self._pkg_use_enabled(node) |
175 |
+ if effective_use != node.use.enabled: |
176 |
+ node._metadata['USE'] = ' '.join(effective_use) |
177 |
+ |
178 |
def _apply_parent_use_changes(self): |
179 |
""" |
180 |
For parents with unsatisfied conditional dependencies, translate |
181 |
@@ -7994,14 +8015,19 @@ class depgraph(object): |
182 |
|
183 |
return display(self, mylist, favorites, verbosity) |
184 |
|
185 |
- def _display_autounmask(self): |
186 |
+ def _display_autounmask(self, autounmask_continue=False): |
187 |
""" |
188 |
Display --autounmask message and optionally write it to config files |
189 |
(using CONFIG_PROTECT). The message includes the comments and the changes. |
190 |
""" |
191 |
|
192 |
+ if self._dynamic_config._displayed_autounmask: |
193 |
+ return |
194 |
+ |
195 |
+ self._dynamic_config._displayed_autounmask = True |
196 |
+ |
197 |
ask = "--ask" in self._frozen_config.myopts |
198 |
- autounmask_write = \ |
199 |
+ autounmask_write = autounmask_continue or \ |
200 |
self._frozen_config.myopts.get("--autounmask-write", |
201 |
ask) is True |
202 |
autounmask_unrestricted_atoms = \ |
203 |
@@ -8286,7 +8312,7 @@ class depgraph(object): |
204 |
writemsg(format_msg(license_msg[root]), noiselevel=-1) |
205 |
|
206 |
protect_obj = {} |
207 |
- if write_to_file: |
208 |
+ if write_to_file and not autounmask_continue: |
209 |
for root in roots: |
210 |
settings = self._frozen_config.roots[root].settings |
211 |
protect_obj[root] = ConfigProtect( |
212 |
@@ -8313,7 +8339,8 @@ class depgraph(object): |
213 |
(file_to_write_to, e)) |
214 |
if file_contents is not None: |
215 |
file_contents.extend(changes) |
216 |
- if protect_obj[root].isprotected(file_to_write_to): |
217 |
+ if (not autounmask_continue and |
218 |
+ protect_obj[root].isprotected(file_to_write_to)): |
219 |
# We want to force new_protect_filename to ensure |
220 |
# that the user will see all our changes via |
221 |
# dispatch-conf, even if file_to_write_to doesn't |
222 |
@@ -8372,6 +8399,8 @@ class depgraph(object): |
223 |
elif write_to_file and roots: |
224 |
writemsg("\nAutounmask changes successfully written.\n", |
225 |
noiselevel=-1) |
226 |
+ if autounmask_continue: |
227 |
+ return True |
228 |
for root in roots: |
229 |
chk_updated_cfg_files(root, |
230 |
[os.path.join(os.sep, USER_CONFIG_PATH)]) |
231 |
@@ -8893,6 +8922,9 @@ class depgraph(object): |
232 |
return self._dynamic_config._success_without_autounmask or \ |
233 |
self._dynamic_config._required_use_unsatisfied |
234 |
|
235 |
+ def need_config_reload(self): |
236 |
+ return self._dynamic_config._need_config_reload |
237 |
+ |
238 |
def autounmask_breakage_detected(self): |
239 |
try: |
240 |
for pargs, kwargs in self._dynamic_config._unsatisfied_deps_for_display: |
241 |
|
242 |
diff --git a/pym/_emerge/main.py b/pym/_emerge/main.py |
243 |
index 5dbafee..0e672a2 100644 |
244 |
--- a/pym/_emerge/main.py |
245 |
+++ b/pym/_emerge/main.py |
246 |
@@ -127,6 +127,7 @@ def insert_optional_args(args): |
247 |
'--alert' : y_or_n, |
248 |
'--ask' : y_or_n, |
249 |
'--autounmask' : y_or_n, |
250 |
+ '--autounmask-continue' : y_or_n, |
251 |
'--autounmask-only' : y_or_n, |
252 |
'--autounmask-keep-masks': y_or_n, |
253 |
'--autounmask-unrestricted-atoms' : y_or_n, |
254 |
@@ -324,6 +325,11 @@ def parse_opts(tmpcmdline, silent=False): |
255 |
"choices" : true_y_or_n |
256 |
}, |
257 |
|
258 |
+ "--autounmask-continue": { |
259 |
+ "help" : "write autounmask changes and continue", |
260 |
+ "choices" : true_y_or_n |
261 |
+ }, |
262 |
+ |
263 |
"--autounmask-only": { |
264 |
"help" : "only perform --autounmask", |
265 |
"choices" : true_y_or_n |
266 |
@@ -751,6 +757,9 @@ def parse_opts(tmpcmdline, silent=False): |
267 |
if myoptions.autounmask in true_y: |
268 |
myoptions.autounmask = True |
269 |
|
270 |
+ if myoptions.autounmask_continue in true_y: |
271 |
+ myoptions.autounmask_continue = True |
272 |
+ |
273 |
if myoptions.autounmask_only in true_y: |
274 |
myoptions.autounmask_only = True |
275 |
else: |
276 |
|
277 |
diff --git a/pym/portage/tests/emerge/test_simple.py b/pym/portage/tests/emerge/test_simple.py |
278 |
index e5ecd4b..b1a2af5 100644 |
279 |
--- a/pym/portage/tests/emerge/test_simple.py |
280 |
+++ b/pym/portage/tests/emerge/test_simple.py |
281 |
@@ -109,6 +109,16 @@ pkg_preinst() { |
282 |
"LICENSE": "GPL-2", |
283 |
"MISC_CONTENT": install_something, |
284 |
}, |
285 |
+ "dev-libs/C-1": { |
286 |
+ "EAPI" : "6", |
287 |
+ "KEYWORDS": "~x86", |
288 |
+ "RDEPEND": "dev-libs/D[flag]", |
289 |
+ }, |
290 |
+ "dev-libs/D-1": { |
291 |
+ "EAPI" : "6", |
292 |
+ "KEYWORDS": "~x86", |
293 |
+ "IUSE" : "flag", |
294 |
+ }, |
295 |
"virtual/foo-0": { |
296 |
"EAPI" : "5", |
297 |
"KEYWORDS": "x86", |
298 |
@@ -301,6 +311,11 @@ pkg_preinst() { |
299 |
emerge_cmd + ("--unmerge", "--quiet", "dev-libs/A"), |
300 |
emerge_cmd + ("-C", "--quiet", "dev-libs/B"), |
301 |
|
302 |
+ emerge_cmd + ("--autounmask-continue", "dev-libs/C",), |
303 |
+ # Verify that the above --autounmask-continue command caused |
304 |
+ # USE=flag to be applied correctly to dev-libs/D. |
305 |
+ portageq_cmd + ("match", eroot, "dev-libs/D[flag]"), |
306 |
+ |
307 |
# Test cross-prefix usage, including chpathtool for binpkgs. |
308 |
({"EPREFIX" : cross_prefix},) + \ |
309 |
emerge_cmd + ("--usepkgonly", "dev-libs/A"), |