Gentoo Archives: gentoo-portage-dev

From: Zac Medico <zmedico@g.o>
To: gentoo-portage-dev@l.g.o
Cc: Zac Medico <zmedico@g.o>
Subject: [gentoo-portage-dev] [PATCH v2] Add emerge --autounmask-continue option (bug 582624)
Date: Sat, 02 Jul 2016 05:19:51
Message-Id: 1467436771-4107-1-git-send-email-zmedico@gentoo.org
In Reply to: [gentoo-portage-dev] [PATCH] Add emerge --autounmask-continue option (bug 582624) by Zac Medico
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