aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZac Medico <zmedico@gentoo.org>2016-06-30 23:48:03 -0700
committerZac Medico <zmedico@gentoo.org>2016-07-02 14:04:47 -0700
commite2d88ef3ff5958571ef2da5f69e8756522390f0f (patch)
tree16bacfd9e5f466a18ed87ca5bf54d90f5053f633
parenttestpath: fix missed newline (diff)
downloadportage-e2d88ef3ff5958571ef2da5f69e8756522390f0f.tar.gz
portage-e2d88ef3ff5958571ef2da5f69e8756522390f0f.tar.bz2
portage-e2d88ef3ff5958571ef2da5f69e8756522390f0f.zip
Add emerge --autounmask-continue option (bug 582624)
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 Acked-by: Brian Dolbec <dolsen@gentoo.org>
-rw-r--r--man/emerge.114
-rw-r--r--pym/_emerge/actions.py36
-rw-r--r--pym/_emerge/depgraph.py40
-rw-r--r--pym/_emerge/main.py9
-rw-r--r--pym/portage/tests/emerge/test_simple.py15
5 files changed, 102 insertions, 12 deletions
diff --git a/man/emerge.1 b/man/emerge.1
index 75862d7be..da1d852ed 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 2ca790272..1dc2b0d13 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 c1c37b4f1..fc957f59d 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
@@ -4179,11 +4181,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
@@ -7994,14 +8015,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 = \
@@ -8286,7 +8312,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(
@@ -8313,7 +8339,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
@@ -8372,6 +8399,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)])
@@ -8893,6 +8922,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 5dbafee77..0e672a22e 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 e5ecd4b7e..b1a2af52f 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"),