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


Reply via email to