1 |
This option will cause emerge to automatically apply autounmask changes |
2 |
to configuration files, and continue to execute the specified command. |
3 |
If the dependency calculation is not entirely successful, then emerge |
4 |
will simply abort without modifying any configuration files. |
5 |
|
6 |
This sort of behavior can be very useful in a continuous integration |
7 |
setting, where the emerge invocation might be inside of a container that |
8 |
is later discarded (so there is no threat of negative consequences). |
9 |
It's also safe for general use, when combined with the --ask option. |
10 |
|
11 |
X-Gentoo-Bug: 582624 |
12 |
X-Gentoo-Bug-url: https://bugs.gentoo.org/show_bug.cgi?id=582624 |
13 |
--- |
14 |
[PATCH v2] changes: |
15 |
|
16 |
* fix depgraph to update USE state of Package instances to be consistent |
17 |
with config file changes |
18 |
|
19 |
* fix load_emerge_config to update existing RootConfig instances |
20 |
in-place, so that the update propagates globally (to depgraph and the |
21 |
Package instances it contains) |
22 |
|
23 |
* include a use configuration change in the unit test, and assert that |
24 |
it is applied correctly |
25 |
|
26 |
man/emerge.1 | 14 +++++++++++- |
27 |
pym/_emerge/actions.py | 36 +++++++++++++++++++++++------ |
28 |
pym/_emerge/depgraph.py | 40 +++++++++++++++++++++++++++++---- |
29 |
pym/_emerge/main.py | 9 ++++++++ |
30 |
pym/portage/tests/emerge/test_simple.py | 15 +++++++++++++ |
31 |
5 files changed, 102 insertions(+), 12 deletions(-) |
32 |
|
33 |
diff --git a/man/emerge.1 b/man/emerge.1 |
34 |
index bfa2f73..40be14f 100644 |
35 |
--- a/man/emerge.1 |
36 |
+++ b/man/emerge.1 |
37 |
@@ -1,4 +1,4 @@ |
38 |
-.TH "EMERGE" "1" "Feb 2016" "Portage VERSION" "Portage" |
39 |
+.TH "EMERGE" "1" "Jul 2016" "Portage VERSION" "Portage" |
40 |
.SH "NAME" |
41 |
emerge \- Command\-line interface to the Portage system |
42 |
.SH "SYNOPSIS" |
43 |
@@ -361,6 +361,18 @@ the specified configuration file(s), or enable the |
44 |
\fBEMERGE_DEFAULT_OPTS\fR variable may be used to |
45 |
disable this option by default in \fBmake.conf\fR(5). |
46 |
.TP |
47 |
+.BR "\-\-autounmask\-continue [ y | n ]" |
48 |
+Automatically apply autounmask changes to configuration |
49 |
+files, and continue to execute the specified command. If |
50 |
+the dependency calculation is not entirely successful, then |
51 |
+emerge will simply abort without modifying any configuration |
52 |
+files. |
53 |
+\fBWARNING:\fR |
54 |
+This option is intended to be used only with great caution, |
55 |
+since it is possible for it to make nonsensical configuration |
56 |
+changes which may lead to system breakage. Therefore, it is |
57 |
+advisable to use \fB\-\-ask\fR together with this option. |
58 |
+.TP |
59 |
.BR "\-\-autounmask\-only [ y | n ]" |
60 |
Instead of doing any package building, just unmask |
61 |
packages and generate package.use settings as necessary |
62 |
diff --git a/pym/_emerge/actions.py b/pym/_emerge/actions.py |
63 |
index 2ca7902..1dc2b0d 100644 |
64 |
--- a/pym/_emerge/actions.py |
65 |
+++ b/pym/_emerge/actions.py |
66 |
@@ -96,8 +96,22 @@ if sys.hexversion >= 0x3000000: |
67 |
else: |
68 |
_unicode = unicode |
69 |
|
70 |
-def action_build(settings, trees, mtimedb, |
71 |
- myopts, myaction, myfiles, spinner): |
72 |
+def action_build(emerge_config, trees=DeprecationWarning, |
73 |
+ mtimedb=DeprecationWarning, myopts=DeprecationWarning, |
74 |
+ myaction=DeprecationWarning, myfiles=DeprecationWarning, spinner=None): |
75 |
+ |
76 |
+ if not isinstance(emerge_config, _emerge_config): |
77 |
+ warnings.warn("_emerge.actions.action_build() now expects " |
78 |
+ "an _emerge_config instance as the first parameter", |
79 |
+ DeprecationWarning, stacklevel=2) |
80 |
+ emerge_config = load_emerge_config( |
81 |
+ action=myaction, args=myfiles, trees=trees, opts=myopts) |
82 |
+ adjust_configs(emerge_config.opts, emerge_config.trees) |
83 |
+ |
84 |
+ settings, trees, mtimedb = emerge_config |
85 |
+ myopts = emerge_config.opts |
86 |
+ myaction = emerge_config.action |
87 |
+ myfiles = emerge_config.args |
88 |
|
89 |
if '--usepkgonly' not in myopts: |
90 |
old_tree_timestamp_warn(settings['PORTDIR'], settings) |
91 |
@@ -327,6 +341,11 @@ def action_build(settings, trees, mtimedb, |
92 |
display_missing_pkg_set(root_config, e.value) |
93 |
return 1 |
94 |
|
95 |
+ if success and mydepgraph.need_config_reload(): |
96 |
+ load_emerge_config(emerge_config=emerge_config) |
97 |
+ adjust_configs(emerge_config.opts, emerge_config.trees) |
98 |
+ settings, trees, mtimedb = emerge_config |
99 |
+ |
100 |
if "--autounmask-only" in myopts: |
101 |
mydepgraph.display_problems() |
102 |
return 0 |
103 |
@@ -2384,7 +2403,13 @@ def load_emerge_config(emerge_config=None, **kargs): |
104 |
settings = root_trees["vartree"].settings |
105 |
settings._init_dirs() |
106 |
setconfig = load_default_config(settings, root_trees) |
107 |
- root_trees["root_config"] = RootConfig(settings, root_trees, setconfig) |
108 |
+ root_config = RootConfig(settings, root_trees, setconfig) |
109 |
+ if "root_config" in root_trees: |
110 |
+ # Propagate changes to the existing instance, |
111 |
+ # which may be referenced by a depgraph. |
112 |
+ root_trees["root_config"].update(root_config) |
113 |
+ else: |
114 |
+ root_trees["root_config"] = root_config |
115 |
|
116 |
target_eroot = emerge_config.trees._target_eroot |
117 |
emerge_config.target_config = \ |
118 |
@@ -3230,10 +3255,7 @@ def run_action(emerge_config): |
119 |
except OSError: |
120 |
writemsg("Please install eselect to use this feature.\n", |
121 |
noiselevel=-1) |
122 |
- retval = action_build(emerge_config.target_config.settings, |
123 |
- emerge_config.trees, emerge_config.target_config.mtimedb, |
124 |
- emerge_config.opts, emerge_config.action, |
125 |
- emerge_config.args, spinner) |
126 |
+ retval = action_build(emerge_config, spinner=spinner) |
127 |
post_emerge(emerge_config.action, emerge_config.opts, |
128 |
emerge_config.args, emerge_config.target_config.root, |
129 |
emerge_config.trees, emerge_config.target_config.mtimedb, retval) |
130 |
diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py |
131 |
index f78f08d..005164e 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 |
@@ -4158,11 +4160,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 |
@@ -7973,14 +7994,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 |
@@ -8265,7 +8291,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 |
@@ -8292,7 +8318,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 |
@@ -8351,6 +8378,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 |
@@ -8872,6 +8901,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 |
diff --git a/pym/_emerge/main.py b/pym/_emerge/main.py |
242 |
index 5dbafee..0e672a2 100644 |
243 |
--- a/pym/_emerge/main.py |
244 |
+++ b/pym/_emerge/main.py |
245 |
@@ -127,6 +127,7 @@ def insert_optional_args(args): |
246 |
'--alert' : y_or_n, |
247 |
'--ask' : y_or_n, |
248 |
'--autounmask' : y_or_n, |
249 |
+ '--autounmask-continue' : y_or_n, |
250 |
'--autounmask-only' : y_or_n, |
251 |
'--autounmask-keep-masks': y_or_n, |
252 |
'--autounmask-unrestricted-atoms' : y_or_n, |
253 |
@@ -324,6 +325,11 @@ def parse_opts(tmpcmdline, silent=False): |
254 |
"choices" : true_y_or_n |
255 |
}, |
256 |
|
257 |
+ "--autounmask-continue": { |
258 |
+ "help" : "write autounmask changes and continue", |
259 |
+ "choices" : true_y_or_n |
260 |
+ }, |
261 |
+ |
262 |
"--autounmask-only": { |
263 |
"help" : "only perform --autounmask", |
264 |
"choices" : true_y_or_n |
265 |
@@ -751,6 +757,9 @@ def parse_opts(tmpcmdline, silent=False): |
266 |
if myoptions.autounmask in true_y: |
267 |
myoptions.autounmask = True |
268 |
|
269 |
+ if myoptions.autounmask_continue in true_y: |
270 |
+ myoptions.autounmask_continue = True |
271 |
+ |
272 |
if myoptions.autounmask_only in true_y: |
273 |
myoptions.autounmask_only = True |
274 |
else: |
275 |
diff --git a/pym/portage/tests/emerge/test_simple.py b/pym/portage/tests/emerge/test_simple.py |
276 |
index e5ecd4b..b1a2af5 100644 |
277 |
--- a/pym/portage/tests/emerge/test_simple.py |
278 |
+++ b/pym/portage/tests/emerge/test_simple.py |
279 |
@@ -109,6 +109,16 @@ pkg_preinst() { |
280 |
"LICENSE": "GPL-2", |
281 |
"MISC_CONTENT": install_something, |
282 |
}, |
283 |
+ "dev-libs/C-1": { |
284 |
+ "EAPI" : "6", |
285 |
+ "KEYWORDS": "~x86", |
286 |
+ "RDEPEND": "dev-libs/D[flag]", |
287 |
+ }, |
288 |
+ "dev-libs/D-1": { |
289 |
+ "EAPI" : "6", |
290 |
+ "KEYWORDS": "~x86", |
291 |
+ "IUSE" : "flag", |
292 |
+ }, |
293 |
"virtual/foo-0": { |
294 |
"EAPI" : "5", |
295 |
"KEYWORDS": "x86", |
296 |
@@ -301,6 +311,11 @@ pkg_preinst() { |
297 |
emerge_cmd + ("--unmerge", "--quiet", "dev-libs/A"), |
298 |
emerge_cmd + ("-C", "--quiet", "dev-libs/B"), |
299 |
|
300 |
+ emerge_cmd + ("--autounmask-continue", "dev-libs/C",), |
301 |
+ # Verify that the above --autounmask-continue command caused |
302 |
+ # USE=flag to be applied correctly to dev-libs/D. |
303 |
+ portageq_cmd + ("match", eroot, "dev-libs/D[flag]"), |
304 |
+ |
305 |
# Test cross-prefix usage, including chpathtool for binpkgs. |
306 |
({"EPREFIX" : cross_prefix},) + \ |
307 |
emerge_cmd + ("--usepkgonly", "dev-libs/A"), |
308 |
-- |
309 |
2.7.4 |