This option will cause emerge to automatically apply autounmask changes to configuration files, and continue to execute the specified command. If the dependency calculation is not entirely successful, then emerge will simply abort without modifying any configuration files.
This sort of behavior can be very useful in a continuous integration setting, where the emerge invocation might be inside of a container that is later discarded (so there is no threat of negative consequences). It's also safe for general use, when combined with the --ask option. X-Gentoo-Bug: 582624 X-Gentoo-Bug-url: https://bugs.gentoo.org/show_bug.cgi?id=582624 --- [PATCH v2] changes: * fix depgraph to update USE state of Package instances to be consistent with config file changes * fix load_emerge_config to update existing RootConfig instances in-place, so that the update propagates globally (to depgraph and the Package instances it contains) * include a use configuration change in the unit test, and assert that it is applied correctly man/emerge.1 | 14 +++++++++++- pym/_emerge/actions.py | 36 +++++++++++++++++++++++------ pym/_emerge/depgraph.py | 40 +++++++++++++++++++++++++++++---- pym/_emerge/main.py | 9 ++++++++ pym/portage/tests/emerge/test_simple.py | 15 +++++++++++++ 5 files changed, 102 insertions(+), 12 deletions(-) diff --git a/man/emerge.1 b/man/emerge.1 index bfa2f73..40be14f 100644 --- a/man/emerge.1 +++ b/man/emerge.1 @@ -1,4 +1,4 @@ -.TH "EMERGE" "1" "Feb 2016" "Portage VERSION" "Portage" +.TH "EMERGE" "1" "Jul 2016" "Portage VERSION" "Portage" .SH "NAME" emerge \- Command\-line interface to the Portage system .SH "SYNOPSIS" @@ -361,6 +361,18 @@ the specified configuration file(s), or enable the \fBEMERGE_DEFAULT_OPTS\fR variable may be used to disable this option by default in \fBmake.conf\fR(5). .TP +.BR "\-\-autounmask\-continue [ y | n ]" +Automatically apply autounmask changes to configuration +files, and continue to execute the specified command. If +the dependency calculation is not entirely successful, then +emerge will simply abort without modifying any configuration +files. +\fBWARNING:\fR +This option is intended to be used only with great caution, +since it is possible for it to make nonsensical configuration +changes which may lead to system breakage. Therefore, it is +advisable to use \fB\-\-ask\fR together with this option. +.TP .BR "\-\-autounmask\-only [ y | n ]" Instead of doing any package building, just unmask packages and generate package.use settings as necessary diff --git a/pym/_emerge/actions.py b/pym/_emerge/actions.py index 2ca7902..1dc2b0d 100644 --- a/pym/_emerge/actions.py +++ b/pym/_emerge/actions.py @@ -96,8 +96,22 @@ if sys.hexversion >= 0x3000000: else: _unicode = unicode -def action_build(settings, trees, mtimedb, - myopts, myaction, myfiles, spinner): +def action_build(emerge_config, trees=DeprecationWarning, + mtimedb=DeprecationWarning, myopts=DeprecationWarning, + myaction=DeprecationWarning, myfiles=DeprecationWarning, spinner=None): + + if not isinstance(emerge_config, _emerge_config): + warnings.warn("_emerge.actions.action_build() now expects " + "an _emerge_config instance as the first parameter", + DeprecationWarning, stacklevel=2) + emerge_config = load_emerge_config( + action=myaction, args=myfiles, trees=trees, opts=myopts) + adjust_configs(emerge_config.opts, emerge_config.trees) + + settings, trees, mtimedb = emerge_config + myopts = emerge_config.opts + myaction = emerge_config.action + myfiles = emerge_config.args if '--usepkgonly' not in myopts: old_tree_timestamp_warn(settings['PORTDIR'], settings) @@ -327,6 +341,11 @@ def action_build(settings, trees, mtimedb, display_missing_pkg_set(root_config, e.value) return 1 + if success and mydepgraph.need_config_reload(): + load_emerge_config(emerge_config=emerge_config) + adjust_configs(emerge_config.opts, emerge_config.trees) + settings, trees, mtimedb = emerge_config + if "--autounmask-only" in myopts: mydepgraph.display_problems() return 0 @@ -2384,7 +2403,13 @@ def load_emerge_config(emerge_config=None, **kargs): settings = root_trees["vartree"].settings settings._init_dirs() setconfig = load_default_config(settings, root_trees) - root_trees["root_config"] = RootConfig(settings, root_trees, setconfig) + root_config = RootConfig(settings, root_trees, setconfig) + if "root_config" in root_trees: + # Propagate changes to the existing instance, + # which may be referenced by a depgraph. + root_trees["root_config"].update(root_config) + else: + root_trees["root_config"] = root_config target_eroot = emerge_config.trees._target_eroot emerge_config.target_config = \ @@ -3230,10 +3255,7 @@ def run_action(emerge_config): except OSError: writemsg("Please install eselect to use this feature.\n", noiselevel=-1) - retval = action_build(emerge_config.target_config.settings, - emerge_config.trees, emerge_config.target_config.mtimedb, - emerge_config.opts, emerge_config.action, - emerge_config.args, spinner) + retval = action_build(emerge_config, spinner=spinner) post_emerge(emerge_config.action, emerge_config.opts, emerge_config.args, emerge_config.target_config.root, emerge_config.trees, emerge_config.target_config.mtimedb, retval) diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py index f78f08d..005164e 100644 --- a/pym/_emerge/depgraph.py +++ b/pym/_emerge/depgraph.py @@ -431,6 +431,7 @@ class _dynamic_depgraph_config(object): self._slot_operator_replace_installed = backtrack_parameters.slot_operator_replace_installed self._prune_rebuilds = backtrack_parameters.prune_rebuilds self._need_restart = False + self._need_config_reload = False # For conditions that always require user intervention, such as # unsatisfied REQUIRED_USE (currently has no autounmask support). self._skip_restart = False @@ -438,6 +439,7 @@ class _dynamic_depgraph_config(object): self._buildpkgonly_deps_unsatisfied = False self._autounmask = depgraph._frozen_config.myopts.get('--autounmask') != 'n' + self._displayed_autounmask = False self._success_without_autounmask = False self._required_use_unsatisfied = False self._traverse_ignored_deps = False @@ -4158,11 +4160,30 @@ class depgraph(object): self._dynamic_config._needed_license_changes) : #We failed if the user needs to change the configuration self._dynamic_config._success_without_autounmask = True + if (self._frozen_config.myopts.get("--autounmask-continue") is True and + "--pretend" not in self._frozen_config.myopts): + # This will return false if it fails or if the user + # aborts via --ask. + if self._display_autounmask(autounmask_continue=True): + self._apply_autounmask_continue_state() + self._dynamic_config._need_config_reload = True + return True, myfavorites return False, myfavorites # We're true here unless we are missing binaries. return (True, myfavorites) + def _apply_autounmask_continue_state(self): + """ + Apply autounmask changes to Package instances, so that their + state will be consistent configuration file changes. + """ + for node in self._dynamic_config._serialized_tasks_cache: + if isinstance(node, Package): + effective_use = self._pkg_use_enabled(node) + if effective_use != node.use.enabled: + node._metadata['USE'] = ' '.join(effective_use) + def _apply_parent_use_changes(self): """ For parents with unsatisfied conditional dependencies, translate @@ -7973,14 +7994,19 @@ class depgraph(object): return display(self, mylist, favorites, verbosity) - def _display_autounmask(self): + def _display_autounmask(self, autounmask_continue=False): """ Display --autounmask message and optionally write it to config files (using CONFIG_PROTECT). The message includes the comments and the changes. """ + if self._dynamic_config._displayed_autounmask: + return + + self._dynamic_config._displayed_autounmask = True + ask = "--ask" in self._frozen_config.myopts - autounmask_write = \ + autounmask_write = autounmask_continue or \ self._frozen_config.myopts.get("--autounmask-write", ask) is True autounmask_unrestricted_atoms = \ @@ -8265,7 +8291,7 @@ class depgraph(object): writemsg(format_msg(license_msg[root]), noiselevel=-1) protect_obj = {} - if write_to_file: + if write_to_file and not autounmask_continue: for root in roots: settings = self._frozen_config.roots[root].settings protect_obj[root] = ConfigProtect( @@ -8292,7 +8318,8 @@ class depgraph(object): (file_to_write_to, e)) if file_contents is not None: file_contents.extend(changes) - if protect_obj[root].isprotected(file_to_write_to): + if (not autounmask_continue and + protect_obj[root].isprotected(file_to_write_to)): # We want to force new_protect_filename to ensure # that the user will see all our changes via # dispatch-conf, even if file_to_write_to doesn't @@ -8351,6 +8378,8 @@ class depgraph(object): elif write_to_file and roots: writemsg("\nAutounmask changes successfully written.\n", noiselevel=-1) + if autounmask_continue: + return True for root in roots: chk_updated_cfg_files(root, [os.path.join(os.sep, USER_CONFIG_PATH)]) @@ -8872,6 +8901,9 @@ class depgraph(object): return self._dynamic_config._success_without_autounmask or \ self._dynamic_config._required_use_unsatisfied + def need_config_reload(self): + return self._dynamic_config._need_config_reload + def autounmask_breakage_detected(self): try: for pargs, kwargs in self._dynamic_config._unsatisfied_deps_for_display: diff --git a/pym/_emerge/main.py b/pym/_emerge/main.py index 5dbafee..0e672a2 100644 --- a/pym/_emerge/main.py +++ b/pym/_emerge/main.py @@ -127,6 +127,7 @@ def insert_optional_args(args): '--alert' : y_or_n, '--ask' : y_or_n, '--autounmask' : y_or_n, + '--autounmask-continue' : y_or_n, '--autounmask-only' : y_or_n, '--autounmask-keep-masks': y_or_n, '--autounmask-unrestricted-atoms' : y_or_n, @@ -324,6 +325,11 @@ def parse_opts(tmpcmdline, silent=False): "choices" : true_y_or_n }, + "--autounmask-continue": { + "help" : "write autounmask changes and continue", + "choices" : true_y_or_n + }, + "--autounmask-only": { "help" : "only perform --autounmask", "choices" : true_y_or_n @@ -751,6 +757,9 @@ def parse_opts(tmpcmdline, silent=False): if myoptions.autounmask in true_y: myoptions.autounmask = True + if myoptions.autounmask_continue in true_y: + myoptions.autounmask_continue = True + if myoptions.autounmask_only in true_y: myoptions.autounmask_only = True else: diff --git a/pym/portage/tests/emerge/test_simple.py b/pym/portage/tests/emerge/test_simple.py index e5ecd4b..b1a2af5 100644 --- a/pym/portage/tests/emerge/test_simple.py +++ b/pym/portage/tests/emerge/test_simple.py @@ -109,6 +109,16 @@ pkg_preinst() { "LICENSE": "GPL-2", "MISC_CONTENT": install_something, }, + "dev-libs/C-1": { + "EAPI" : "6", + "KEYWORDS": "~x86", + "RDEPEND": "dev-libs/D[flag]", + }, + "dev-libs/D-1": { + "EAPI" : "6", + "KEYWORDS": "~x86", + "IUSE" : "flag", + }, "virtual/foo-0": { "EAPI" : "5", "KEYWORDS": "x86", @@ -301,6 +311,11 @@ pkg_preinst() { emerge_cmd + ("--unmerge", "--quiet", "dev-libs/A"), emerge_cmd + ("-C", "--quiet", "dev-libs/B"), + emerge_cmd + ("--autounmask-continue", "dev-libs/C",), + # Verify that the above --autounmask-continue command caused + # USE=flag to be applied correctly to dev-libs/D. + portageq_cmd + ("match", eroot, "dev-libs/D[flag]"), + # Test cross-prefix usage, including chpathtool for binpkgs. ({"EPREFIX" : cross_prefix},) + \ emerge_cmd + ("--usepkgonly", "dev-libs/A"), -- 2.7.4