Merged from trunk 11267:11284
authorFabian Groffen <grobian@gentoo.org>
Wed, 30 Jul 2008 13:58:43 +0000 (13:58 -0000)
committerFabian Groffen <grobian@gentoo.org>
Wed, 30 Jul 2008 13:58:43 +0000 (13:58 -0000)
   | 11268   | Add a quiet signal handler for SIGINT and SIGTERM since      |
   | zmedico | emerge calls ebuild for fetchs and we don't want the user to |
   |         | see a traceback due to the ebuild process getting killed.    |

   | 11269   | Disable auto_schedule on all of Scheduler._task_queues and   |
   | zmedico | schedule them inside _schedule_tasks(). This should help     |
   |         | solve a tight loop.                                          |

   | 11270   | Fix the last part of Scheduler._main_loop() to work          |
   | zmedico | correctly now that all the queues have auto_schedule         |
   |         | disabled.                                                    |

   | 11271   | Avoid triggering a tight loop AssertionError in              |
   | zmedico | Scheduler._main_loop().                                      |

   | 11272   | Fix _choose_pkg() to always return something if nothing is   |
   | zmedico | running or merging.                                          |

   | 11273   | Count state changes in Scheduler._schedule_tasks() and avoid |
   | zmedico | some extra calls when nothing changes.                       |

   | 11274   | Change conditional USE deps syntax and expermental EAPI from |
   | zmedico | 2_pre0 to 2_pre1. Conditional syntax examples: Compact Form  |
   |         | Equivalent Expanded Form foo[bar?] bar? ( foo[bar] ) !bar? ( |
   |         | foo ) foo[!bar?] bar? ( foo ) !bar? ( foo[-bar] ) foo[bar=]  |
   |         | bar? ( foo[bar] ) !bar? ( foo[-bar] ) foo[!bar=] bar? (      |
   |         | foo[-bar] ) !bar? ( foo[bar] )                               |

   | 11275   | * Optimize SequentialTaskQueue.schedule() by using a _dirty  |
   | zmedico | attribute to track whether anything relevant has changed     |
   |         | since the last schedule call. Since we can always rely on    |
   |         | exist listeners being called, set of running tasks is always |
   |         | pruned automatically and there is never any need to actively |
   |         | prune it. * Remove the unused                                |
   |         | SequentialTaskQueue.auto_schedule feature.                   |

   | 11276   | Just return a boolean "state changed" value from             |
   | zmedico | Scheduler._schedule_tasks_imp(), since the other "remaining" |
   |         | part is now unused.                                          |

   | 11277   | Put a 5 second cap on the delay produced by                  |
   | zmedico | Scheduler._job_delay().                                      |

   | 11278   | When checking for news items in --pretend mode, pass         |
   | zmedico | update=False into the NewsManager.getUnreadItems() call.     |
   |         | Emerge shouldn't have to update anything anyway when in      |
   |         | --pretend mode, and this helps to avoid potential permission |
   |         | problems. In order to pass the relevant information about    |
   |         | options (--pretend state) to the new code, supporting        |
   |         | function parameters have been added to                       |
   |         | display_news_notification(), post_emerge(), and              |
   |         | checkUpdatedNewsItems().                                     |

   | 11279   | * Add more use dep validation. * Fix broken handling of      |
   | zmedico | !foo? reported by ABCD.                                      |

   | 11280   | Remove unused variable.                                      |
   | zmedico |                                                              |

   | 11281   | Remove redundant use dep validation code that's handled by   |
   | zmedico | regular expression now.                                      |

   | 11282   | * Fix Scheduler._restart_if_necessary() breakage so that the |
   | zmedico | given package is correctly compared to the last one in the   |
   |         | merge list. * Update code in depgraph.display() to be more   |
   |         | consistent with the code in                                  |
   |         | Scheduler._is_restart_necessary().                           |

   | 11283   | Update foo[!bar=] syntax.                                    |
   | zmedico |                                                              |

   | 11284   | Fix typo.                                                    |
   | zmedico |                                                              |

svn path=/main/branches/prefix/; revision=11287

bin/ebuild
doc/package/ebuild/eapi/2.docbook
pym/_emerge/__init__.py
pym/portage/__init__.py
pym/portage/dep.py
pym/portage/tests/__init__.py
pym/portage/tests/dep/test_isvalidatom.py

index 4a4f0a0345c6d09eef2c8ba6c1adaa85d15e4c0b..0e48ab8e695bfef5189ff32a651e31b352f787a5 100755 (executable)
@@ -3,9 +3,24 @@
 # Distributed under the terms of the GNU General Public License v2
 # $Header: /var/cvsroot/gentoo-src/portage/bin/ebuild,v 1.18.2.3 2005/05/07 04:32:59 ferringb Exp $
 
+import sys
+# This block ensures that ^C interrupts are handled quietly.
+try:
+       import signal
+
+       def exithandler(signum,frame):
+               signal.signal(signal.SIGINT, signal.SIG_IGN)
+               signal.signal(signal.SIGTERM, signal.SIG_IGN)
+               sys.exit(1)
+
+       signal.signal(signal.SIGINT, exithandler)
+       signal.signal(signal.SIGTERM, exithandler)
+
+except KeyboardInterrupt:
+       sys.exit(1)
+
 import optparse
 import os
-import sys
 
 description = "See the ebuild(1) man page for more info"
 usage = "Usage: ebuild <ebuild file> <command> [command] ..."
index 08386cc2f25f275716a34c33290fc9ed1a13c3e8..2b1fa56980bd06f0f33dcfd88cb72b41719a9b81 100644 (file)
@@ -1,5 +1,5 @@
 <sect1 id='package-ebuild-eapi-2'>
-       <title>EAPI 2_pre0</title>
+       <title>EAPI 2_pre1</title>
        <sect2 id='package-ebuild-eapi-2-helpers'>
        <title>Helpers</title>
        <sect3 id='package-ebuild-eapi-2-helpers-doman'>
@@ -80,7 +80,7 @@
        <entry>bar? ( foo[bar]  ) !bar? ( foo       )</entry>
        </row>
        <row>
-       <entry>foo[-bar?]</entry>
+       <entry>foo[!bar?]</entry>
        <entry>bar? ( foo       ) !bar? ( foo[-bar] )</entry>
        </row>
        <row>
@@ -88,8 +88,8 @@
        <entry>bar? ( foo[bar]  ) !bar? ( foo[-bar] )</entry>
        </row>
        <row>
-       <entry>foo[bar!=]</entry>
-       <entry>bar? ( foo[-bar] ) !bar? ( foo[bar] )</entry>
+       <entry>foo[!bar=]</entry>
+       <entry>bar? ( foo[-bar] ) !bar? ( foo[bar]  )</entry>
        </row>
        </tbody>
        </tgroup>
index d142b498b040dd54c5d6dd2fb82b42204c754005..6faf517a250140e7f63c07816fb2a79255b3ff3b 100644 (file)
@@ -4083,6 +4083,8 @@ class depgraph(object):
                if settings.get("PORTAGE_DEBUG", "") == "1":
                        self.edebug = 1
                self.spinner = spinner
+               self._running_root = trees["/"]["root_config"]
+               self._opts_no_restart = Scheduler._opts_no_restart
                self.pkgsettings = {}
                # Maps slot atom to package for each Package added to the graph.
                self._slot_pkg_map = {}
@@ -6142,7 +6144,7 @@ class depgraph(object):
                                        node in scheduled_uninstalls)]
 
                # sys-apps/portage needs special treatment if ROOT="/"
-               running_root = "/"
+               running_root = self._running_root.root
                from portage.const import PORTAGE_PACKAGE_ATOM
                runtime_deps = InternalPackageSet(
                        initial_atoms=[PORTAGE_PACKAGE_ATOM])
@@ -7351,23 +7353,23 @@ class depgraph(object):
                                                        myprint="["+pkgprint(pkg_type)+" "+addl+"] "+indent+pkgprint(pkg_key)+" "+myoldbest+" "+verboseadd
                                p.append(myprint)
 
-                               mysplit = [portage.cpv_getkey(pkg_key)] + \
-                                       list(portage.catpkgsplit(pkg_key)[2:])
-                               if "--tree" not in self.myopts and mysplit and \
-                                       len(mysplit) == 3 and mysplit[0] == "sys-apps/portage" and \
-                                       x[1] == "/":
-       
-                                       if mysplit[2] == "r0":
-                                               myversion = mysplit[1]
+                               if "--tree" not in self.myopts and \
+                                       "--quiet" not in self.myopts and \
+                                       not self._opts_no_restart.intersection(self.myopts) and \
+                                       pkg.root == self._running_root.root and \
+                                       portage.match_from_list(
+                                       portage.const.PORTAGE_PACKAGE_ATOM, [pkg]):
+
+                                       pn, ver, rev = pkg.pv_split
+                                       if rev == "r0":
+                                               myversion = ver
                                        else:
-                                               myversion = "%s-%s" % (mysplit[1], mysplit[2])
-       
+                                               myversion = "%s-%s" % (ver, rev)
+
                                        if myversion != portage.VERSION and "--quiet" not in self.myopts:
                                                if mylist_index < len(mylist) - 1:
                                                        p.append(colorize("WARN", "*** Portage will stop merging at this point and reload itself,"))
                                                        p.append(colorize("WARN", "    then resume the merge."))
-                                                       print
-                               del mysplit
 
                for x in p:
                        print x
@@ -8153,8 +8155,8 @@ class PollSelectAdapter(PollConstants):
 
 class SequentialTaskQueue(SlotObject):
 
-       __slots__ = ("auto_schedule", "max_jobs", "running_tasks") + \
-               ("_task_queue", "_scheduling")
+       __slots__ = ("max_jobs", "running_tasks") + \
+               ("_dirty", "_scheduling", "_task_queue")
 
        def __init__(self, **kwargs):
                SlotObject.__init__(self, **kwargs)
@@ -8162,19 +8164,21 @@ class SequentialTaskQueue(SlotObject):
                self.running_tasks = set()
                if self.max_jobs is None:
                        self.max_jobs = 1
+               self._dirty = True
 
        def add(self, task):
                self._task_queue.append(task)
-               if self.auto_schedule:
-                       self.schedule()
+               self._dirty = True
 
        def addFront(self, task):
                self._task_queue.appendleft(task)
-               if self.auto_schedule:
-                       self.schedule()
+               self._dirty = True
 
        def schedule(self):
 
+               if not self._dirty:
+                       return False
+
                if not self:
                        return False
 
@@ -8190,10 +8194,6 @@ class SequentialTaskQueue(SlotObject):
                max_jobs = self.max_jobs
                state_changed = False
 
-               for task in list(running_tasks):
-                       if task.poll() is not None:
-                               state_changed = True
-
                while task_queue and \
                        (max_jobs is True or len(running_tasks) < max_jobs):
                        task = task_queue.popleft()
@@ -8204,14 +8204,20 @@ class SequentialTaskQueue(SlotObject):
                                task.start()
                        state_changed = True
 
+               self._dirty = False
                self._scheduling = False
 
                return state_changed
 
        def _task_exit(self, task):
-               self.running_tasks.discard(task)
-               if self.auto_schedule:
-                       self.schedule()
+               """
+               Since we can always rely on exit listeners being called, the set of
+               running tasks is always pruned automatically and there is never any need
+               to actively prune it.
+               """
+               self.running_tasks.remove(task)
+               if self._task_queue:
+                       self._dirty = True
 
        def clear(self):
                self._task_queue.clear()
@@ -8220,6 +8226,7 @@ class SequentialTaskQueue(SlotObject):
                        task = running_tasks.pop()
                        task.removeExitListener(self._task_exit)
                        task.cancel()
+               self._dirty = False
 
        def __nonzero__(self):
                return bool(self._task_queue or self.running_tasks)
@@ -8867,13 +8874,7 @@ class Scheduler(PollScheduler):
                self._task_queues = self._task_queues_class()
                for k in self._task_queues.allowed_keys:
                        setattr(self._task_queues, k,
-                               SequentialTaskQueue(auto_schedule=True))
-
-               # Merge tasks currently run synchronously which makes
-               # it necessary to disable auto_schedule in order to
-               # avoid excess recursion which prevents tasks from
-               # being marked complete as soon as they should be.
-               self._task_queues.merge.auto_schedule = False
+                               SequentialTaskQueue())
 
                self._prefetchers = weakref.WeakValueDictionary()
                self._pkg_queue = []
@@ -8904,6 +8905,7 @@ class Scheduler(PollScheduler):
                # The load average takes some time to respond when new
                # jobs are added, so we need to limit the rate of adding
                # new jobs.
+               self._job_delay_max = 5
                self._job_delay_factor = 0.5
                self._previous_job_start_time = None
 
@@ -9322,7 +9324,7 @@ class Scheduler(PollScheduler):
                if not self._is_restart_necessary(pkg):
                        return
 
-               if self._pkg_count.curval >= self._pkg_count.maxval:
+               if pkg == self._mergelist[-1]:
                        return
 
                self._main_loop_cleanup()
@@ -9616,6 +9618,9 @@ class Scheduler(PollScheduler):
                                return None
                        return self._pkg_queue.pop(0)
 
+               if not (self._jobs or self._task_queues.merge):
+                       return self._pkg_queue.pop(0)
+
                self._prune_digraph()
 
                chosen_pkg = None
@@ -9687,26 +9692,38 @@ class Scheduler(PollScheduler):
                merge_queue = self._task_queues.merge
 
                while self._schedule():
-                       self._poll_loop()
+                       if self._poll_event_handlers:
+                               self._poll_loop()
 
-               while self._jobs or merge_queue:
-                       if merge_queue.schedule() and \
-                               not self._poll_event_handlers:
-                               continue
-                       self._poll_loop()
+               while True:
+                       self._schedule()
+                       if not self._jobs or merge_queue:
+                               break
+                       if self._poll_event_handlers:
+                               self._poll_loop()
 
        def _schedule_tasks(self):
-               self._task_queues.merge.schedule()
+               self._schedule_tasks_imp()
+               self._status_display.display()
+
+               state_change = 0
+               for q in self._task_queues.values():
+                       if q.schedule():
+                               state_change += 1
 
                # Cancel prefetchers if they're the only reason
                # the main poll loop is still running.
                if self._failed_pkgs and \
-                       not (self._jobs or self._task_queues.merge):
+                       not (self._jobs or self._task_queues.merge) and \
+                       self._task_queues.fetch:
                        self._task_queues.fetch.clear()
+                       state_change += 1
 
-               remaining, state_change = self._schedule_tasks_imp()
-               self._status_display.display()
-               return remaining
+               if state_change:
+                       self._schedule_tasks_imp()
+                       self._status_display.display()
+
+               return bool(self._pkg_queue and not self._failed_pkgs)
 
        def _job_delay(self):
                """
@@ -9718,8 +9735,10 @@ class Scheduler(PollScheduler):
 
                        current_time = time.time()
 
-                       if current_time - self._previous_job_start_time < \
-                               self._job_delay_factor * self._jobs:
+                       delay = self._job_delay_factor * self._jobs
+                       if delay > self._job_delay_max:
+                               delay = self._job_delay_max
+                       if (current_time - self._previous_job_start_time) < delay:
                                return True
 
                return False
@@ -9727,7 +9746,7 @@ class Scheduler(PollScheduler):
        def _schedule_tasks_imp(self):
                """
                @rtype: bool
-               @returns: True if tasks remain to schedule, False otherwise.
+               @returns: True if state changed, False otherwise.
                """
 
                state_change = 0
@@ -9735,16 +9754,16 @@ class Scheduler(PollScheduler):
                while True:
 
                        if not self._pkg_queue or self._failed_pkgs:
-                               return (False, state_change)
+                               return bool(state_change)
 
                        if self._choose_pkg_return_early or \
                                not self._can_add_job() or \
                                self._job_delay():
-                               return (True, state_change)
+                               return bool(state_change)
 
                        pkg = self._choose_pkg()
                        if pkg is None:
-                               return (True, state_change)
+                               return bool(state_change)
 
                        state_change += 1
 
@@ -9772,7 +9791,7 @@ class Scheduler(PollScheduler):
                                task.addExitListener(self._build_exit)
                                self._task_queues.jobs.add(task)
 
-               return (True, state_change)
+               return bool(state_change)
 
        def _task(self, pkg):
 
@@ -10653,20 +10672,20 @@ def chk_updated_info_files(root, infodirs, prev_mtimes, retval):
                                        print " "+green("*")+" Processed",icount,"info files."
 
 
-def display_news_notification(trees):
-       for target_root in trees:
-               if len(trees) > 1 and target_root != "/":
-                       break
-       settings = trees[target_root]["vartree"].settings
-       portdb = trees[target_root]["porttree"].dbapi
-       vardb = trees[target_root]["vartree"].dbapi
+def display_news_notification(root_config, myopts):
+       target_root = root_config.root
+       trees = root_config.trees
+       settings = trees["vartree"].settings
+       portdb = trees["porttree"].dbapi
+       vardb = trees["vartree"].dbapi
        NEWS_PATH = os.path.join("metadata", "news")
        UNREAD_PATH = os.path.join(target_root, NEWS_LIB_PATH, "news")
        newsReaderDisplay = False
+       update = "--pretend" not in myopts
 
        for repo in portdb.getRepositories():
                unreadItems = checkUpdatedNewsItems(
-                       portdb, vardb, NEWS_PATH, UNREAD_PATH, repo)
+                       portdb, vardb, NEWS_PATH, UNREAD_PATH, repo, update=update)
                if unreadItems:
                        if not newsReaderDisplay:
                                newsReaderDisplay = True
@@ -10697,7 +10716,7 @@ def _flush_elog_mod_echo():
                mod_echo.finalize()
        return messages_shown
 
-def post_emerge(trees, mtimedb, retval):
+def post_emerge(root_config, myopts, mtimedb, retval):
        """
        Misc. things to run at the end of a merge session.
        
@@ -10718,9 +10737,9 @@ def post_emerge(trees, mtimedb, retval):
        @returns:
        1.  Calls sys.exit(retval)
        """
-       for target_root in trees:
-               if len(trees) > 1 and target_root != "/":
-                       break
+
+       target_root = root_config.root
+       trees = { target_root : root_config.trees }
        vardbapi = trees[target_root]["vartree"].dbapi
        settings = vardbapi.settings
        info_mtimes = mtimedb["info"]
@@ -10763,7 +10782,7 @@ def post_emerge(trees, mtimedb, retval):
 
        chk_updated_cfg_files(target_root + EPREFIX, config_protect)
        
-       display_news_notification(trees)
+       display_news_notification(root_config, myopts)
        
        if vardbapi.plib_registry.hasEntries():
                print
@@ -10832,7 +10851,8 @@ def chk_updated_cfg_files(target_root, config_protect):
                                " section of the " + bold("emerge")
                        print " "+yellow("*")+" man page to learn how to update config files."
 
-def checkUpdatedNewsItems(portdb, vardb, NEWS_PATH, UNREAD_PATH, repo_id):
+def checkUpdatedNewsItems(portdb, vardb, NEWS_PATH, UNREAD_PATH, repo_id,
+       update=False):
        """
        Examines news items in repodir + '/' + NEWS_PATH and attempts to find unread items
        Returns the number of unread (yet relevent) items.
@@ -10854,7 +10874,7 @@ def checkUpdatedNewsItems(portdb, vardb, NEWS_PATH, UNREAD_PATH, repo_id):
        """
        from portage.news import NewsManager
        manager = NewsManager(portdb, vardb, NEWS_PATH, UNREAD_PATH)
-       return manager.getUnreadItems( repo_id, update=True )
+       return manager.getUnreadItems( repo_id, update=update )
 
 def insert_category_into_atom(atom, category):
        alphanum = re.search(r'\w', atom)
@@ -11357,6 +11377,7 @@ def action_sync(settings, trees, mtimedb, myopts, myaction):
 
        # Reload the whole config from scratch.
        settings, trees, mtimedb = load_emerge_config(trees=trees)
+       root_config = trees[settings["ROOT"]]["root_config"]
        portdb = trees[settings["ROOT"]]["porttree"].dbapi
 
        if os.path.exists(myportdir+"/metadata/cache") and updatecache_flg:
@@ -11390,7 +11411,7 @@ def action_sync(settings, trees, mtimedb, myopts, myaction):
                print red(" * ")+"To update portage, run 'emerge portage' now."
                print
        
-       display_news_notification(trees)
+       display_news_notification(root_config, myopts)
 
 def action_metadata(settings, portdb, myopts):
        portage.writemsg_stdout("\n>>> Updating Portage cache:      ")
@@ -12838,7 +12859,8 @@ def action_build(settings, trees, mtimedb,
                        trees[settings["ROOT"]]["vartree"].dbapi.plib_registry.pruneNonExisting()
 
                if merge_count and not (buildpkgonly or fetchonly or pretend):
-                       post_emerge(trees, mtimedb, retval)
+                       root_config = trees[settings["ROOT"]]["root_config"]
+                       post_emerge(root_config, myopts, mtimedb, retval)
                return retval
 
 def multiple_actions(action1, action2):
@@ -13601,6 +13623,9 @@ def emerge_main():
                        sys.stderr.write(("emerge: The '%s' action does " + \
                                "not support '--pretend'.\n") % myaction)
                        return 1
+
+       root_config = trees[settings["ROOT"]]["root_config"]
+
        if "sync" == myaction:
                action_sync(settings, trees, mtimedb, myopts, myaction)
        elif "metadata" == myaction:
@@ -13626,30 +13651,29 @@ def emerge_main():
        elif myaction in ("clean", "unmerge") or \
                (myaction == "prune" and "--nodeps" in myopts):
                validate_ebuild_environment(trees)
-               root_config = trees[settings["ROOT"]]["root_config"]
                # When given a list of atoms, unmerge
                # them in the order given.
                ordered = myaction == "unmerge"
                if 1 == unmerge(root_config, myopts, myaction, myfiles,
                        mtimedb["ldpath"], ordered=ordered):
                        if not (buildpkgonly or fetchonly or pretend):
-                               post_emerge(trees, mtimedb, os.EX_OK)
+                               post_emerge(root_config, myopts, mtimedb, os.EX_OK)
 
        elif myaction in ("depclean", "prune"):
                validate_ebuild_environment(trees)
                action_depclean(settings, trees, mtimedb["ldpath"],
                        myopts, myaction, myfiles, spinner)
                if not (buildpkgonly or fetchonly or pretend):
-                       post_emerge(trees, mtimedb, os.EX_OK)
+                       post_emerge(root_config, myopts, mtimedb, os.EX_OK)
        # "update", "system", or just process files:
        else:
                validate_ebuild_environment(trees)
                if "--pretend" not in myopts:
-                       display_news_notification(trees)
+                       display_news_notification(root_config, myopts)
                retval = action_build(settings, trees, mtimedb,
                        myopts, myaction, myfiles, spinner)
                # if --pretend was not enabled then display_news_notification 
                # was already called by post_emerge
                if "--pretend" in myopts:
-                       display_news_notification(trees)
+                       display_news_notification(root_config, myopts)
                return retval
index e6a3d169535d9fa1d6da943d3837e0f5390fca6e..264fb47f295eac593f0206e8706c7ba19f24da27 100644 (file)
@@ -4525,7 +4525,7 @@ def eapi_is_supported(eapi):
                        eapi.remove(prop)
        
        # now check if what's left is supported (can)
-       properties = [ "2_pre0" ] # another clumpsy solution
+       properties = [ "2_pre1" ] # another clumpsy solution
        for i in range(portage.const.EAPI + 1):
                properties.append(str(i))
 
index 8528da4f8f5b27665eb7a626e4266e02ead15dcf..adbde5f6df6cdd7ab2d0213634d7efd2c1cae94a 100644 (file)
@@ -347,6 +347,8 @@ class _use_dep(object):
        _conditionals_class = portage.cache.mappings.slot_dict_class(
                ("disabled", "enabled", "equal", "not_equal"), prefix="")
 
+       _valid_use_re = re.compile(r'^[^-?!=][^?!=]*$')
+
        def __init__(self, use):
                enabled_flags = []
                disabled_flags = []
@@ -356,23 +358,29 @@ class _use_dep(object):
 
                for x in use:
                        last_char = x[-1:]
+                       first_char = x[:1]
+
                        if "?" == last_char:
-                               if "-" == x[:1]:
-                                       conditional.disabled.append(x[1:-1])
+                               if "!" == first_char:
+                                       conditional.disabled.append(
+                                               self._validate_flag(x, x[1:-1]))
                                else:
-                                       conditional.enabled.append(x[:-1])
+                                       conditional.enabled.append(
+                                               self._validate_flag(x, x[:-1]))
+
                        elif "=" == last_char:
-                               if "-" == x[:1]:
-                                       raise InvalidAtom("Invalid use dep: '%s'" % (x,))
-                               if "!" == x[-2:-1]:
-                                       conditional.not_equal.append(x[:-2])
+                               if "!" == first_char:
+                                       conditional.not_equal.append(
+                                               self._validate_flag(x, x[1:-1]))
                                else:
-                                       conditional.equal.append(x[:-1])
+                                       conditional.equal.append(
+                                               self._validate_flag(x, x[:-1]))
+
                        else:
-                               if "-" == x[:1]:
-                                       disabled_flags.append(x[1:])
+                               if "-" == first_char:
+                                       disabled_flags.append(self._validate_flag(x, x[1:]))
                                else:
-                                       enabled_flags.append(x)
+                                       enabled_flags.append(self._validate_flag(x, x))
 
                self.tokens = use
                if not isinstance(self.tokens, tuple):
@@ -395,6 +403,11 @@ class _use_dep(object):
                                self.conditional = conditional
                                break
 
+       def _validate_flag(self, token, flag):
+               if self._valid_use_re.match(flag) is None:
+                       raise InvalidAtom("Invalid use dep: '%s'" % (token,))
+               return flag
+
        def __nonzero__(self):
                return bool(self.tokens)
 
@@ -413,22 +426,22 @@ class _use_dep(object):
 
                         x              x?            x
                        -x              x?
-                        x             -x?
-                       -x             -x?           -x
+                        x             !x?
+                       -x             !x?           -x
 
                         x              x=            x
                        -x              x=           -x
-                        x              x!=          -x
-                       -x              x!=           x
+                        x             !x=           -x
+                       -x             !x=            x
 
                Conditional syntax examples:
 
-                       compact form         equivalent expanded form
+                       Compact Form        Equivalent Expanded Form
 
                        foo[bar?]           bar? ( foo[bar]  ) !bar? ( foo       )
-                       foo[-bar?]          bar? ( foo       ) !bar? ( foo[-bar] )
+                       foo[!bar?]          bar? ( foo       ) !bar? ( foo[-bar] )
                        foo[bar=]           bar? ( foo[bar]  ) !bar? ( foo[-bar] )
-                       foo[bar!=]          bar? ( foo[-bar] ) !bar? ( foo[bar]  )
+                       foo[!bar=]          bar? ( foo[-bar] ) !bar? ( foo[bar]  )
 
                """
                tokens = []
index 7ca048b9ac6b0f974374b37447d3080338b56dfb..927af49c74804e338746c539326c4945a883977a 100644 (file)
@@ -178,4 +178,4 @@ test_cps = ['sys-apps/portage','virtual/portage']
 test_versions = ['1.0', '1.0-r1','2.3_p4','1.0_alpha57']
 test_slots = [ None, '1','gentoo-sources-2.6.17','spankywashere']
 test_usedeps = ['foo','-bar', ('foo','bar'),
-       ('foo','-bar'), ('foo?', '-bar?') ]
+       ('foo','-bar'), ('foo?', '!bar?') ]
index f7cc3b844e8533751372a328c3fd7a1f1c39813a..cd501e90da717faa56596e1f016f108a1240d6df 100644 (file)
@@ -25,16 +25,24 @@ class IsValidAtom(TestCase):
                          ( "sys-apps/portage:foo", True ),
                          ( "sys-apps/portage-2.1:foo", False ),
                          ( "sys-apps/portage-2.1:", False ),
-                         ( "=sys-apps/portage-2.2*:foo[bar?,-baz?,doc!=,build=]", True ),
-                         ( "=sys-apps/portage-2.2*:foo[build=]", True ),
-                         ( "=sys-apps/portage-2.2*:foo[doc!=]", True ),
+                         ( "=sys-apps/portage-2.2*:foo[bar?,!baz?,!doc=,build=]", True ),
+                         ( "=sys-apps/portage-2.2*:foo[doc?]", True ),
+                         ( "=sys-apps/portage-2.2*:foo[!doc?]", True ),
+                         ( "=sys-apps/portage-2.2*:foo[doc=]", True ),
+                         ( "=sys-apps/portage-2.2*:foo[!doc=]", True ),
+                         ( "=sys-apps/portage-2.2*:foo[!doc]", False ),
+                         ( "=sys-apps/portage-2.2*:foo[!-doc]", False ),
+                         ( "=sys-apps/portage-2.2*:foo[!-doc=]", False ),
+                         ( "=sys-apps/portage-2.2*:foo[!-doc?]", False ),
+                         ( "=sys-apps/portage-2.2*:foo[-doc?]", False ),
+                         ( "=sys-apps/portage-2.2*:foo[-doc=]", False ),
                          ( "=sys-apps/portage-2.2*:foo[-doc!=]", False ),
                          ( "=sys-apps/portage-2.2*:foo[-doc=]", False ),
-                         ( "=sys-apps/portage-2.2*:foo[bar][-baz][doc?][-build?]", False ),
-                         ( "=sys-apps/portage-2.2*:foo[bar,-baz,doc?,-build?]", True ),
-                         ( "=sys-apps/portage-2.2*:foo[bar,-baz,doc?,-build?,]", False ),
-                         ( "=sys-apps/portage-2.2*:foo[,bar,-baz,doc?,-build?]", False ),
-                         ( "=sys-apps/portage-2.2*:foo[bar,-baz][doc?,-build?]", False ),
+                         ( "=sys-apps/portage-2.2*:foo[bar][-baz][doc?][!build?]", False ),
+                         ( "=sys-apps/portage-2.2*:foo[bar,-baz,doc?,!build?]", True ),
+                         ( "=sys-apps/portage-2.2*:foo[bar,-baz,doc?,!build?,]", False ),
+                         ( "=sys-apps/portage-2.2*:foo[,bar,-baz,doc?,!build?]", False ),
+                         ( "=sys-apps/portage-2.2*:foo[bar,-baz][doc?,!build?]", False ),
                          ( "=sys-apps/portage-2.2*:foo[bar][doc,build]", False ),
                          ( ">~cate-gory/foo-1.0", False ),
                          ( ">~category/foo-1.0", False ),