get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/1.2/patches/815413/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 815413,
    "url": "http://patchwork.ozlabs.org/api/1.2/patches/815413/?format=api",
    "web_url": "http://patchwork.ozlabs.org/project/uboot/patch/1505810726-28189-1-git-send-email-ulfalizer@gmail.com/",
    "project": {
        "id": 18,
        "url": "http://patchwork.ozlabs.org/api/1.2/projects/18/?format=api",
        "name": "U-Boot",
        "link_name": "uboot",
        "list_id": "u-boot.lists.denx.de",
        "list_email": "u-boot@lists.denx.de",
        "web_url": null,
        "scm_url": null,
        "webscm_url": null,
        "list_archive_url": "",
        "list_archive_url_format": "",
        "commit_url_format": ""
    },
    "msgid": "<1505810726-28189-1-git-send-email-ulfalizer@gmail.com>",
    "list_archive_url": null,
    "date": "2017-09-19T08:45:26",
    "name": "[U-Boot,1/1] kconfiglib: update with 'imply' support",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": false,
    "hash": "db9644ef4c30cfc562008bc321081e92269a4bfd",
    "submitter": {
        "id": 64803,
        "url": "http://patchwork.ozlabs.org/api/1.2/people/64803/?format=api",
        "name": "Ulf Magnusson",
        "email": "ulfalizer@gmail.com"
    },
    "delegate": {
        "id": 3651,
        "url": "http://patchwork.ozlabs.org/api/1.2/users/3651/?format=api",
        "username": "trini",
        "first_name": "Tom",
        "last_name": "Rini",
        "email": "trini@ti.com"
    },
    "mbox": "http://patchwork.ozlabs.org/project/uboot/patch/1505810726-28189-1-git-send-email-ulfalizer@gmail.com/mbox/",
    "series": [
        {
            "id": 3828,
            "url": "http://patchwork.ozlabs.org/api/1.2/series/3828/?format=api",
            "web_url": "http://patchwork.ozlabs.org/project/uboot/list/?series=3828",
            "date": "2017-09-19T08:45:26",
            "name": "[U-Boot,1/1] kconfiglib: update with 'imply' support",
            "version": 1,
            "mbox": "http://patchwork.ozlabs.org/series/3828/mbox/"
        }
    ],
    "comments": "http://patchwork.ozlabs.org/api/patches/815413/comments/",
    "check": "pending",
    "checks": "http://patchwork.ozlabs.org/api/patches/815413/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<u-boot-bounces@lists.denx.de>",
        "X-Original-To": "incoming@patchwork.ozlabs.org",
        "Delivered-To": "patchwork-incoming@bilbo.ozlabs.org",
        "Authentication-Results": [
            "ozlabs.org;\n\tspf=none (mailfrom) smtp.mailfrom=lists.denx.de\n\t(client-ip=81.169.180.215; helo=lists.denx.de;\n\tenvelope-from=u-boot-bounces@lists.denx.de;\n\treceiver=<UNKNOWN>)",
            "ozlabs.org;\n\tdkim=fail reason=\"signature verification failed\" (2048-bit key;\n\tunprotected) header.d=gmail.com header.i=@gmail.com\n\theader.b=\"MY2wQHre\"; dkim-atps=neutral"
        ],
        "Received": [
            "from lists.denx.de (dione.denx.de [81.169.180.215])\n\tby ozlabs.org (Postfix) with ESMTP id 3xxK2n4b2Mz9s82\n\tfor <incoming@patchwork.ozlabs.org>;\n\tTue, 19 Sep 2017 20:33:04 +1000 (AEST)",
            "by lists.denx.de (Postfix, from userid 105)\n\tid EC1B3C21EFF; Tue, 19 Sep 2017 10:33:01 +0000 (UTC)",
            "from lists.denx.de (localhost [IPv6:::1])\n\tby lists.denx.de (Postfix) with ESMTP id 7AD70C21C5C;\n\tTue, 19 Sep 2017 10:32:53 +0000 (UTC)",
            "by lists.denx.de (Postfix, from userid 105)\n\tid 228A8C21D93; Tue, 19 Sep 2017 08:45:48 +0000 (UTC)",
            "from mail-lf0-f50.google.com (mail-lf0-f50.google.com\n\t[209.85.215.50])\n\tby lists.denx.de (Postfix) with ESMTPS id 5C420C21D70\n\tfor <u-boot@lists.denx.de>; Tue, 19 Sep 2017 08:45:47 +0000 (UTC)",
            "by mail-lf0-f50.google.com with SMTP id m199so2965579lfe.3\n\tfor <u-boot@lists.denx.de>; Tue, 19 Sep 2017 01:45:47 -0700 (PDT)",
            "from huvuddator.lan (ua-213-112-0-22.cust.bredbandsbolaget.se.\n\t[213.112.0.22]) by smtp.gmail.com with ESMTPSA id\n\tq18sm2305427lja.19.2017.09.19.01.45.45\n\t(version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128);\n\tTue, 19 Sep 2017 01:45:45 -0700 (PDT)"
        ],
        "X-Spam-Checker-Version": "SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de",
        "X-Spam-Level": "",
        "X-Spam-Status": "No, score=-0.0 required=5.0 tests=FREEMAIL_FROM,\n\tRCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL,\n\tT_DKIM_INVALID\n\tautolearn=unavailable autolearn_force=no version=3.4.0",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025;\n\th=from:to:cc:subject:date:message-id;\n\tbh=NYflGgEunsSymJJSdM4MJuLqI8sru5kD7hA95X4Ct5U=;\n\tb=MY2wQHreUBz8CDVR1blTqmLeDTu4Lix25/K/47Fg5Bov4xj3jyZ8fQYvN89UtTWbFY\n\t6kHdB3twm8wGufvEHo4HDnyzfUb5UPKVTR+sN1CVgL02baALKuh6K9JqhBUcTUFZNpfE\n\tgwxbxWGgVWjMoPTbbiZ0bahJKe+4kQ6ujR4HXI9GQ0avpLsgWmRT7LOQCct0ScR/R/tP\n\tW2SPly9pScVuyasTrhAXp0iUaNtciSS8fstLArFR31bNIu/wMQ5gHpTDtIpTYQfuqMwT\n\t9YIP44lVGRFZZndwLFmzujQaXMvejDf6ABxighlVPgO7gJLUKyrJpb0X0KF6RA6jKVhl\n\tGwTA==",
        "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20161025;\n\th=x-gm-message-state:from:to:cc:subject:date:message-id;\n\tbh=NYflGgEunsSymJJSdM4MJuLqI8sru5kD7hA95X4Ct5U=;\n\tb=mU+SNEIregCRXT0Dp/Yt6WG+IDHvWUZI59j/vTcUNUbLd3Q2JTU83yWbLMHtDkOgbf\n\tczGk+93leCVW+E/6HhcXp19z7gZvF7qXCl4Nn8XKLhT2ajexMhLFSa7EDUYTwi7YRhMh\n\tG9ZNkV6R57+cLV4LlxuWLmKf95zBh+mF0UMMg6X43RmZoOdaCqck826LzqMPoNNdpXjw\n\tbg0d8ejTJxFWbOR3LlrRir8eIbbkXmgnjXC6jp81MCLsMPNcQc5rmYWR3TdLzDqIOHvt\n\ti4icKT7idR35HsgCQBtCSQdojZALErlBEBSFkU5RkjM2Aenv9Q76f4u/RFbdnfdVttcb\n\tQIfQ==",
        "X-Gm-Message-State": "AHPjjUhCc5bhfTYu93MXhuhyrvwzz35Duj1FF6xQNAoa8RpEUrtr7Vhg\n\tdVNVdCTZQ9zm1RDUwMrunu4/jpfZ",
        "X-Google-Smtp-Source": "AOwi7QDqonViV2+KRhVWZxmNIWBLV4VFX/7T5wLjJbWcRnSsKXoQ7wUK65QA3xrsrSQP4IL2dRGuMA==",
        "X-Received": "by 10.25.142.215 with SMTP id a84mr321967lfl.25.1505810746283;\n\tTue, 19 Sep 2017 01:45:46 -0700 (PDT)",
        "From": "Ulf Magnusson <ulfalizer@gmail.com>",
        "To": "u-boot@lists.denx.de",
        "Date": "Tue, 19 Sep 2017 10:45:26 +0200",
        "Message-Id": "<1505810726-28189-1-git-send-email-ulfalizer@gmail.com>",
        "X-Mailer": "git-send-email 2.7.4",
        "X-Mailman-Approved-At": "Tue, 19 Sep 2017 10:32:51 +0000",
        "Subject": "[U-Boot] [PATCH 1/1] kconfiglib: update with 'imply' support",
        "X-BeenThere": "u-boot@lists.denx.de",
        "X-Mailman-Version": "2.1.18",
        "Precedence": "list",
        "List-Id": "U-Boot discussion <u-boot.lists.denx.de>",
        "List-Unsubscribe": "<https://lists.denx.de/options/u-boot>,\n\t<mailto:u-boot-request@lists.denx.de?subject=unsubscribe>",
        "List-Archive": "<http://lists.denx.de/pipermail/u-boot/>",
        "List-Post": "<mailto:u-boot@lists.denx.de>",
        "List-Help": "<mailto:u-boot-request@lists.denx.de?subject=help>",
        "List-Subscribe": "<https://lists.denx.de/listinfo/u-boot>,\n\t<mailto:u-boot-request@lists.denx.de?subject=subscribe>",
        "MIME-Version": "1.0",
        "Content-Type": "text/plain; charset=\"utf-8\"",
        "Content-Transfer-Encoding": "base64",
        "Errors-To": "u-boot-bounces@lists.denx.de",
        "Sender": "\"U-Boot\" <u-boot-bounces@lists.denx.de>"
    },
    "content": "Corresponds to 375506d (File writing nit) from upstream\n(https://github.com/ulfalizer/Kconfiglib).\n\nAdds proper 'imply' support and fixes a few minor issues, one of which\npreviously triggered the following weird warning:\n\n  configs/taurus_defconfig: /tmp/tmpisI45S:6: warning: assignment to SPL_LDSCRIPT changes mode of containing choice from \"arch/$(ARCH)/cpu/u-boot-spl.lds\" to \"y\"\n\nThe change in 8639f69 (genconfig.py: Print defconfig next to warnings)\nhas been reapplied.\n\ntools/genboardscfg.py was verified to produce identical board.cfg's\nbefore and after the change.\n\nSigned-off-by: Ulf Magnusson <ulfalizer@gmail.com>\n---\n tools/buildman/kconfiglib.py | 333 +++++++++++++++++++++++++------------------\n 1 file changed, 193 insertions(+), 140 deletions(-)",
    "diff": "diff --git a/tools/buildman/kconfiglib.py b/tools/buildman/kconfiglib.py\nindex 352ad43..68b470a 100644\n--- a/tools/buildman/kconfiglib.py\n+++ b/tools/buildman/kconfiglib.py\n@@ -73,6 +73,7 @@ email service. Don't wrestle with internal APIs. Tell me what you need and I\n might add it in a safe way as a client API instead.\"\"\"\n \n import os\n+import platform\n import re\n import sys\n \n@@ -137,10 +138,8 @@ class Config(object):\n         # The set of all symbols, indexed by name (a string)\n         self.syms = {}\n         # Python 2/3 compatibility hack. This is the only one needed.\n-        if sys.version_info[0] >= 3:\n-            self.syms_iter = self.syms.values\n-        else:\n-            self.syms_iter = self.syms.itervalues\n+        self.syms_iter = self.syms.values if sys.version_info[0] >= 3 else \\\n+                         self.syms.itervalues\n \n         # The set of all defined symbols in the configuration in the order they\n         # appear in the Kconfig files. This excludes the special symbols n, m,\n@@ -173,7 +172,7 @@ class Config(object):\n         self.m = register_special_symbol(TRISTATE, \"m\", \"m\")\n         self.y = register_special_symbol(TRISTATE, \"y\", \"y\")\n         # DEFCONFIG_LIST uses this\n-        register_special_symbol(STRING, \"UNAME_RELEASE\", os.uname()[2])\n+        register_special_symbol(STRING, \"UNAME_RELEASE\", platform.uname()[2])\n \n         # The symbol with \"option defconfig_list\" set, containing a list of\n         # default .config files\n@@ -183,16 +182,20 @@ class Config(object):\n         self.arch = os.environ.get(\"ARCH\")\n         self.srcarch = os.environ.get(\"SRCARCH\")\n \n+        # If you set CONFIG_ in the environment, Kconfig will prefix all symbols\n+        # with its value when saving the configuration, instead of using the default, \"CONFIG_\".\n+        self.config_prefix = os.environ.get(\"CONFIG_\")\n+        if self.config_prefix is None:\n+            self.config_prefix = \"CONFIG_\"\n+\n         # See Config.__init__(). We need this for get_defconfig_filename().\n         self.srctree = os.environ.get(\"srctree\")\n         if self.srctree is None:\n             self.srctree = \".\"\n \n         self.filename = filename\n-        if base_dir is None:\n-            self.base_dir = self.srctree\n-        else:\n-            self.base_dir = os.path.expandvars(base_dir)\n+        self.base_dir = self.srctree if base_dir is None else \\\n+                        os.path.expandvars(base_dir)\n \n         # The 'mainmenu' text\n         self.mainmenu_text = None\n@@ -222,7 +225,8 @@ class Config(object):\n         self._transform_m = None\n \n         # Parse the Kconfig files\n-        self.top_block = self._parse_file(filename, None, None, None)\n+        self.top_block = []\n+        self._parse_file(filename, None, None, None, self.top_block)\n \n         # Build Symbol.dep for all symbols\n         self._build_dep()\n@@ -405,6 +409,10 @@ class Config(object):\n         \"\"\"\n \n         self._warnings = []\n+        # Regular expressions for parsing .config files\n+        _set_re_match = re.compile(r\"{}(\\w+)=(.*)\".format(self.config_prefix)).match\n+        _unset_re_match = re.compile(r\"# {}(\\w+) is not set\".format(self.config_prefix)).match\n+\n         # Put this first so that a missing file doesn't screw up our state\n         filename = os.path.expandvars(filename)\n         line_feeder = _FileFeed(filename)\n@@ -524,14 +532,12 @@ class Config(object):\n         with open(filename, \"w\") as f:\n             # Write header\n             if header is not None:\n-                f.write(_comment(header))\n-                f.write(\"\\n\")\n+                f.write(_comment(header) + \"\\n\")\n \n             # Build and write configuration\n             conf_strings = []\n             _make_block_conf(self.top_block, conf_strings.append)\n-            f.write(\"\\n\".join(conf_strings))\n-            f.write(\"\\n\")\n+            f.write(\"\\n\".join(conf_strings) + \"\\n\")\n \n     def eval(self, s):\n         \"\"\"Returns the value of the expression 's' -- where 's' is represented\n@@ -609,16 +615,18 @@ class Config(object):\n     # Kconfig parsing\n     #\n \n-    def _parse_file(self, filename, parent, deps, visible_if_deps, res=None):\n-        \"\"\"Parses the Kconfig file 'filename'. Returns a list with the Items in\n-        the file. See _parse_block() for the meaning of the parameters.\"\"\"\n-        return self._parse_block(_FileFeed(filename), None, parent, deps,\n-                                 visible_if_deps, res)\n+    def _parse_file(self, filename, parent, deps, visible_if_deps, block):\n+        \"\"\"Parses the Kconfig file 'filename'. Appends the Items in the file\n+        (and any file it sources) to the list passed in the 'block' parameter.\n+        See _parse_block() for the meaning of the parameters.\"\"\"\n+        self._parse_block(_FileFeed(filename), None, parent, deps,\n+                          visible_if_deps, block)\n \n     def _parse_block(self, line_feeder, end_marker, parent, deps,\n-                     visible_if_deps, res=None):\n+                     visible_if_deps, block):\n         \"\"\"Parses a block, which is the contents of either a file or an if,\n-        menu, or choice statement. Returns a list with the Items in the block.\n+        menu, or choice statement. Appends the Items to the list passed in the\n+        'block' parameter.\n \n         line_feeder: A _FileFeed instance feeding lines from a file. The\n           Kconfig language is line-based in practice.\n@@ -634,10 +642,7 @@ class Config(object):\n         visible_if_deps (default: None): 'visible if' dependencies from\n            enclosing menus.\n \n-        res (default: None): The list to add items to. If None, a new list is\n-           created to hold the items.\"\"\"\n-\n-        block = [] if res is None else res\n+        block: The list to add items to.\"\"\"\n \n         while 1:\n             # Do we already have a tokenized line that we determined wasn't\n@@ -656,7 +661,7 @@ class Config(object):\n                     if end_marker is not None:\n                         raise Kconfig_Syntax_Error(\"Unexpected end of file {0}\"\n                                                  .format(line_feeder.filename))\n-                    return block\n+                    return\n \n                 tokens = self._tokenize(line, False, line_feeder.filename,\n                                         line_feeder.linenr)\n@@ -679,14 +684,13 @@ class Config(object):\n                 # choice statements, the choice statement takes precedence.\n                 if not sym.is_defined_ or isinstance(parent, Choice):\n                     sym.parent = parent\n-\n                 sym.is_defined_ = True\n \n+                self._parse_properties(line_feeder, sym, deps, visible_if_deps)\n+\n                 self.kconfig_syms.append(sym)\n                 block.append(sym)\n \n-                self._parse_properties(line_feeder, sym, deps, visible_if_deps)\n-\n             elif t0 == T_SOURCE:\n                 kconfig_file = tokens.get_next()\n                 exp_kconfig_file = self._expand_sym_refs(kconfig_file)\n@@ -705,7 +709,7 @@ class Config(object):\n \n             elif t0 == end_marker:\n                 # We have reached the end of the block\n-                return block\n+                return\n \n             elif t0 == T_IF:\n                 # If statements are treated as syntactic sugar for adding\n@@ -722,38 +726,39 @@ class Config(object):\n \n             elif t0 == T_COMMENT:\n                 comment = Comment()\n-\n                 comment.config = self\n                 comment.parent = parent\n                 comment.filename = line_feeder.filename\n                 comment.linenr = line_feeder.linenr\n                 comment.text = tokens.get_next()\n \n-                self.comments.append(comment)\n-                block.append(comment)\n-\n                 self._parse_properties(line_feeder, comment, deps,\n                                        visible_if_deps)\n \n+                self.comments.append(comment)\n+                block.append(comment)\n+\n             elif t0 == T_MENU:\n                 menu = Menu()\n-\n                 menu.config = self\n                 menu.parent = parent\n                 menu.filename = line_feeder.filename\n                 menu.linenr = line_feeder.linenr\n                 menu.title = tokens.get_next()\n \n-                self.menus.append(menu)\n-                block.append(menu)\n-\n-                # Parse properties and contents\n                 self._parse_properties(line_feeder, menu, deps,\n                                        visible_if_deps)\n-                menu.block = self._parse_block(line_feeder, T_ENDMENU, menu,\n-                                               menu.dep_expr,\n-                                               _make_and(visible_if_deps,\n-                                                         menu.visible_if_expr))\n+\n+                # This needs to go before _parse_block() so that we get the\n+                # proper menu ordering in the case of nested functions\n+                self.menus.append(menu)\n+                # Parse contents and put Items in menu.block\n+                self._parse_block(line_feeder, T_ENDMENU, menu, menu.dep_expr,\n+                                  _make_and(visible_if_deps,\n+                                            menu.visible_if_expr),\n+                                  menu.block)\n+\n+                block.append(menu)\n \n             elif t0 == T_CHOICE:\n                 name = tokens.get_next()\n@@ -775,11 +780,12 @@ class Config(object):\n                 choice.def_locations.append((line_feeder.filename,\n                                              line_feeder.linenr))\n \n-                # Parse properties and contents\n                 self._parse_properties(line_feeder, choice, deps,\n                                        visible_if_deps)\n-                choice.block = self._parse_block(line_feeder, T_ENDCHOICE,\n-                                                 choice, deps, visible_if_deps)\n+\n+                # Parse contents and put Items in choice.block\n+                self._parse_block(line_feeder, T_ENDCHOICE, choice, deps,\n+                                  visible_if_deps, choice.block)\n \n                 choice._determine_actual_symbols()\n \n@@ -819,19 +825,19 @@ class Config(object):\n             \"\"\"Parses '<expr1> if <expr2>' constructs, where the 'if' part is\n             optional. Returns a tuple containing the parsed expressions, with\n             None as the second element if the 'if' part is missing.\"\"\"\n-            val = self._parse_expr(tokens, stmt, line, filename, linenr, False)\n-            if tokens.check(T_IF):\n-                return (val, self._parse_expr(tokens, stmt, line, filename,\n-                                              linenr))\n-            return (val, None)\n+            return (self._parse_expr(tokens, stmt, line, filename, linenr,\n+                                     False),\n+                    self._parse_expr(tokens, stmt, line, filename, linenr)\n+                    if tokens.check(T_IF) else None)\n \n         # In case the symbol is defined in multiple locations, we need to\n-        # remember what prompts, defaults, and selects are new for this\n-        # definition, as \"depends on\" should only apply to the local\n+        # remember what prompts, defaults, selects, and implies are new for\n+        # this definition, as \"depends on\" should only apply to the local\n         # definition.\n         new_prompt = None\n         new_def_exprs = []\n         new_selects = []\n+        new_implies = []\n \n         # Dependencies from 'depends on' statements\n         depends_on_expr = None\n@@ -897,18 +903,27 @@ class Config(object):\n \n                 line_feeder.unget()\n \n-            elif t0 == T_SELECT or t0 == T_IMPLY:\n+            elif t0 == T_SELECT:\n                 target = tokens.get_next()\n \n                 stmt.referenced_syms.add(target)\n                 stmt.selected_syms.add(target)\n \n-                if tokens.check(T_IF):\n-                    new_selects.append((target,\n-                                        self._parse_expr(tokens, stmt, line,\n-                                                         filename, linenr)))\n-                else:\n-                    new_selects.append((target, None))\n+                new_selects.append(\n+                    (target,\n+                     self._parse_expr(tokens, stmt, line, filename, linenr)\n+                     if tokens.check(T_IF) else None))\n+\n+            elif t0 == T_IMPLY:\n+                target = tokens.get_next()\n+\n+                stmt.referenced_syms.add(target)\n+                stmt.implied_syms.add(target)\n+\n+                new_implies.append(\n+                    (target,\n+                     self._parse_expr(tokens, stmt, line, filename, linenr)\n+                     if tokens.check(T_IF) else None))\n \n             elif t0 in (T_BOOL, T_TRISTATE, T_INT, T_HEX, T_STRING):\n                 stmt.type = TOKEN_TO_TYPE[t0]\n@@ -939,12 +954,10 @@ class Config(object):\n                 stmt.referenced_syms.add(low)\n                 stmt.referenced_syms.add(high)\n \n-                if tokens.check(T_IF):\n-                    stmt.ranges.append((low, high,\n-                                        self._parse_expr(tokens, stmt, line,\n-                                                         filename, linenr)))\n-                else:\n-                    stmt.ranges.append((low, high, None))\n+                stmt.ranges.append(\n+                    (low, high,\n+                     self._parse_expr(tokens, stmt, line, filename, linenr)\n+                     if tokens.check(T_IF) else None))\n \n             elif t0 == T_DEF_TRISTATE:\n                 stmt.type = TRISTATE\n@@ -1051,21 +1064,20 @@ class Config(object):\n             # Symbol or Choice\n \n             # See comment for 'menu_dep'\n-            stmt.menu_dep = depends_on_expr\n+            stmt.menu_dep = _make_and(deps, depends_on_expr)\n \n             # Propagate dependencies to prompts\n \n             if new_prompt is not None:\n-                # Propagate 'visible if' dependencies from enclosing menus\n                 prompt, cond_expr = new_prompt\n-                cond_expr = _make_and(cond_expr, visible_if_deps)\n-                # Propagate 'depends on' dependencies\n-                new_prompt = (prompt, _make_and(cond_expr, depends_on_expr))\n+                # Propagate 'visible if' dependencies from menus and local\n+                # 'depends on' dependencies\n+                cond_expr = _make_and(_make_and(cond_expr, visible_if_deps),\n+                                      depends_on_expr)\n                 # Save original\n-                stmt.orig_prompts.append(new_prompt)\n+                stmt.orig_prompts.append((prompt, cond_expr))\n                 # Finalize with dependencies from enclosing menus and ifs\n-                stmt.prompts.append((new_prompt[0],\n-                                     _make_and(new_prompt[1], deps)))\n+                stmt.prompts.append((prompt, _make_and(cond_expr, deps)))\n \n             # Propagate dependencies to defaults\n \n@@ -1078,20 +1090,27 @@ class Config(object):\n             stmt.def_exprs.extend([(val_expr, _make_and(cond_expr, deps))\n                                    for val_expr, cond_expr in new_def_exprs])\n \n-            # Propagate dependencies to selects\n+            # Propagate dependencies to selects and implies\n \n-            # Only symbols can select\n+            # Only symbols can select and imply\n             if isinstance(stmt, Symbol):\n                 # Propagate 'depends on' dependencies\n                 new_selects = [(target, _make_and(cond_expr, depends_on_expr))\n                                for target, cond_expr in new_selects]\n+                new_implies = [(target, _make_and(cond_expr, depends_on_expr))\n+                               for target, cond_expr in new_implies]\n                 # Save original\n                 stmt.orig_selects.extend(new_selects)\n+                stmt.orig_implies.extend(new_implies)\n                 # Finalize with dependencies from enclosing menus and ifs\n                 for target, cond in new_selects:\n-                    target.rev_dep = _make_or(target.rev_dep,\n-                                              _make_and(stmt,\n-                                                        _make_and(cond, deps)))\n+                    target.rev_dep = \\\n+                        _make_or(target.rev_dep,\n+                                 _make_and(stmt, _make_and(cond, deps)))\n+                for target, cond in new_implies:\n+                    target.weak_rev_dep = \\\n+                        _make_or(target.weak_rev_dep,\n+                                 _make_and(stmt, _make_and(cond, deps)))\n \n     def _parse_expr(self, feed, cur_item, line, filename=None, linenr=None,\n                     transform_m=True):\n@@ -1483,7 +1502,8 @@ class Config(object):\n \n         # The directly dependent symbols of a symbol are:\n         #  - Any symbols whose prompts, default values, rev_dep (select\n-        #    condition), or ranges depend on the symbol\n+        #    condition), weak_rev_dep (imply condition) or ranges depend on the\n+        #    symbol\n         #  - Any symbols that belong to the same choice statement as the symbol\n         #    (these won't be included in 'dep' as that makes the dependency\n         #    graph unwieldy, but Symbol._get_dependent() will include them)\n@@ -1497,6 +1517,7 @@ class Config(object):\n                 add_expr_deps(e, sym)\n \n             add_expr_deps(sym.rev_dep, sym)\n+            add_expr_deps(sym.weak_rev_dep, sym)\n \n             for l, u, e in sym.ranges:\n                 add_expr_deps(l, sym)\n@@ -1625,20 +1646,16 @@ class Config(object):\n         else:\n             prompts_str_rows = []\n             for prompt, cond_expr in sc.orig_prompts:\n-                if cond_expr is None:\n-                    prompts_str_rows.append(' \"{0}\"'.format(prompt))\n-                else:\n-                    prompts_str_rows.append(\n-                      ' \"{0}\" if {1}'.format(prompt,\n-                                             self._expr_val_str(cond_expr)))\n+                prompts_str_rows.append(\n+                    ' \"{0}\"'.format(prompt) if cond_expr is None else\n+                    ' \"{0}\" if {1}'.format(prompt,\n+                                           self._expr_val_str(cond_expr)))\n             prompts_str = \"\\n\".join(prompts_str_rows)\n \n         # Build locations string\n-        if not sc.def_locations:\n-            locations_str = \"(no locations)\"\n-        else:\n-            locations_str = \" \".join([\"{0}:{1}\".format(filename, linenr) for\n-                                      (filename, linenr) in sc.def_locations])\n+        locations_str = \"(no locations)\" if not sc.def_locations else \\\n+                        \" \".join([\"{0}:{1}\".format(filename, linenr) for\n+                                  filename, linenr in sc.def_locations])\n \n         # Build additional-dependencies-from-menus-and-ifs string\n         additional_deps_str = \" \" + \\\n@@ -1657,13 +1674,11 @@ class Config(object):\n                 else:\n                     ranges_str_rows = []\n                     for l, u, cond_expr in sc.ranges:\n-                        if cond_expr is None:\n-                            ranges_str_rows.append(\" [{0}, {1}]\".format(s(l),\n-                                                                        s(u)))\n-                        else:\n-                            ranges_str_rows.append(\" [{0}, {1}] if {2}\"\n-                              .format(s(l), s(u),\n-                                      self._expr_val_str(cond_expr)))\n+                        ranges_str_rows.append(\n+                            \" [{0}, {1}]\".format(s(l), s(u))\n+                            if cond_expr is None else\n+                            \" [{0}, {1}] if {2}\"\n+                            .format(s(l), s(u), self._expr_val_str(cond_expr)))\n                     ranges_str = \"\\n\".join(ranges_str_rows)\n \n             # Build default values string\n@@ -1685,14 +1700,24 @@ class Config(object):\n             else:\n                 selects_str_rows = []\n                 for target, cond_expr in sc.orig_selects:\n-                    if cond_expr is None:\n-                        selects_str_rows.append(\" {0}\".format(target.name))\n-                    else:\n-                        selects_str_rows.append(\n-                          \" {0} if {1}\".format(target.name,\n-                                               self._expr_val_str(cond_expr)))\n+                    selects_str_rows.append(\n+                        \" {0}\".format(target.name) if cond_expr is None else\n+                        \" {0} if {1}\".format(target.name,\n+                                             self._expr_val_str(cond_expr)))\n                 selects_str = \"\\n\".join(selects_str_rows)\n \n+            # Build implies string\n+            if not sc.orig_implies:\n+                implies_str = \" (no implies)\"\n+            else:\n+                implies_str_rows = []\n+                for target, cond_expr in sc.orig_implies:\n+                    implies_str_rows.append(\n+                        \" {0}\".format(target.name) if cond_expr is None else\n+                        \" {0} if {1}\".format(target.name,\n+                                             self._expr_val_str(cond_expr)))\n+                implies_str = \"\\n\".join(implies_str_rows)\n+\n             res = _lines(\"Symbol \" +\n                            (\"(no name)\" if sc.name is None else sc.name),\n                          \"Type           : \" + TYPENAME[sc.type],\n@@ -1711,9 +1736,16 @@ class Config(object):\n                           defaults_str,\n                           \"Selects:\",\n                           selects_str,\n+                          \"Implies:\",\n+                          implies_str,\n                           \"Reverse (select-related) dependencies:\",\n-                          \" (no reverse dependencies)\" if sc.rev_dep == \"n\"\n-                            else \" \" + self._expr_val_str(sc.rev_dep),\n+                          \" (no reverse dependencies)\"\n+                          if sc.rev_dep == \"n\"\n+                          else \" \" + self._expr_val_str(sc.rev_dep),\n+                          \"Weak reverse (imply-related) dependencies:\",\n+                          \" (no weak reverse dependencies)\"\n+                          if sc.weak_rev_dep == \"n\"\n+                          else \" \" + self._expr_val_str(sc.weak_rev_dep),\n                           \"Additional dependencies from enclosing menus \"\n                             \"and ifs:\",\n                           additional_deps_str,\n@@ -1735,11 +1767,10 @@ class Config(object):\n         else:\n             defaults_str_rows = []\n             for sym, cond_expr in sc.orig_def_exprs:\n-                if cond_expr is None:\n-                    defaults_str_rows.append(\" {0}\".format(sym.name))\n-                else:\n-                    defaults_str_rows.append(\" {0} if {1}\".format(sym.name,\n-                                                self._expr_val_str(cond_expr)))\n+                defaults_str_rows.append(\n+                    \" {0}\".format(sym.name) if cond_expr is None else\n+                    \" {0} if {1}\".format(sym.name,\n+                                         self._expr_val_str(cond_expr)))\n             defaults_str = \"\\n\".join(defaults_str_rows)\n \n         # Build contained symbols string\n@@ -1919,26 +1950,25 @@ class Symbol(Item):\n                     self.write_to_conf = (mode != \"n\")\n \n                     if mode == \"y\":\n-                        if choice.get_selection() is self:\n-                            new_val = \"y\"\n-                        else:\n-                            new_val = \"n\"\n+                        new_val = \"y\" if choice.get_selection() is self \\\n+                                  else \"n\"\n                     elif mode == \"m\":\n                         if self.user_val == \"m\" or self.user_val == \"y\":\n                             new_val = \"m\"\n \n             else:\n                 # If the symbol is visible and has a user value, use that.\n-                # Otherwise, look at defaults.\n-                use_defaults = True\n+                # Otherwise, look at defaults and weak reverse dependencies\n+                # (implies).\n+                use_defaults_and_weak_rev_deps = True\n \n                 if vis != \"n\":\n                     self.write_to_conf = True\n                     if self.user_val is not None:\n                         new_val = self.config._eval_min(self.user_val, vis)\n-                        use_defaults = False\n+                        use_defaults_and_weak_rev_deps = False\n \n-                if use_defaults:\n+                if use_defaults_and_weak_rev_deps:\n                     for val_expr, cond_expr in self.def_exprs:\n                         cond_eval = self.config._eval_expr(cond_expr)\n                         if cond_eval != \"n\":\n@@ -1947,14 +1977,25 @@ class Symbol(Item):\n                                                             cond_eval)\n                             break\n \n+                    weak_rev_dep_val = \\\n+                        self.config._eval_expr(self.weak_rev_dep)\n+                    if weak_rev_dep_val != \"n\":\n+                        self.write_to_conf = True\n+                        new_val = self.config._eval_max(new_val,\n+                                                        weak_rev_dep_val)\n+\n                 # Reverse (select-related) dependencies take precedence\n                 rev_dep_val = self.config._eval_expr(self.rev_dep)\n                 if rev_dep_val != \"n\":\n                     self.write_to_conf = True\n                     new_val = self.config._eval_max(new_val, rev_dep_val)\n \n-            # Promote \"m\" to \"y\" for booleans\n-            if new_val == \"m\" and self.type == BOOL:\n+            # We need to promote \"m\" to \"y\" in two circumstances:\n+            #  1) If our type is boolean\n+            #  2) If our weak_rev_dep (from IMPLY) is \"y\"\n+            if new_val == \"m\" and \\\n+               (self.type == BOOL or\n+                self.config._eval_expr(self.weak_rev_dep) == \"y\"):\n                 new_val = \"y\"\n \n         elif self.type == INT or self.type == HEX:\n@@ -2189,6 +2230,13 @@ class Symbol(Item):\n         get_referenced_symbols().\"\"\"\n         return self.selected_syms\n \n+    def get_implied_symbols(self):\n+        \"\"\"Returns the set() of all symbols X for which this symbol has an\n+        'imply X' or 'imply X if Y' (regardless of whether Y is satisfied or\n+        not). This is a subset of the symbols returned by\n+        get_referenced_symbols().\"\"\"\n+        return self.implied_syms\n+\n     def set_user_value(self, v):\n         \"\"\"Sets the user value of the symbol.\n \n@@ -2304,16 +2352,18 @@ class Symbol(Item):\n         self.ranges = [] # 'range' properties (for int and hex)\n         self.help = None # Help text\n         self.rev_dep = \"n\" # Reverse (select-related) dependencies\n+        self.weak_rev_dep = \"n\" # Weak reverse (imply-related) dependencies\n         self.config = None\n         self.parent = None\n \n         self.user_val = None # Value set by user\n \n-        # The prompt, default value and select conditions without any\n+        # The prompt, default value, select, and imply conditions without any\n         # dependencies from menus and ifs propagated to them\n         self.orig_prompts = []\n         self.orig_def_exprs = []\n         self.orig_selects = []\n+        self.orig_implies = []\n \n         # Dependencies inherited from containing menus and ifs\n         self.deps_from_containing = None\n@@ -2323,13 +2373,15 @@ class Symbol(Item):\n         # The set of symbols selected by this symbol (see\n         # get_selected_symbols())\n         self.selected_syms = set()\n+        # The set of symbols implied by this symbol (see get_implied_symbols())\n+        self.implied_syms = set()\n         # Like 'referenced_syms', but includes symbols from\n         # dependencies inherited from enclosing menus and ifs\n         self.all_referenced_syms = set()\n \n-        # This records only dependencies specified with 'depends on'. Needed\n-        # when determining actual choice items (hrrrr...). See also\n-        # Choice._determine_actual_symbols().\n+        # This records only dependencies from enclosing ifs and menus together\n+        # with local 'depends on' dependencies. Needed when determining actual\n+        # choice items (hrrrr...). See Choice._determine_actual_symbols().\n         self.menu_dep = None\n \n         # See Symbol.get_ref/def_locations().\n@@ -2470,18 +2522,17 @@ class Symbol(Item):\n             return\n \n         if self.type == BOOL or self.type == TRISTATE:\n-            if val == \"y\" or val == \"m\":\n-                append_fn(\"CONFIG_{0}={1}\".format(self.name, val))\n-            else:\n-                append_fn(\"# CONFIG_{0} is not set\".format(self.name))\n+            append_fn(\"{0}{1}={2}\".format(self.config.config_prefix, self.name, val)\n+                      if val == \"y\" or val == \"m\" else\n+                      \"# {0}{1} is not set\".format(self.config.config_prefix, self.name))\n \n         elif self.type == INT or self.type == HEX:\n-            append_fn(\"CONFIG_{0}={1}\".format(self.name, val))\n+            append_fn(\"{0}{1}={2}\".format(self.config.config_prefix, self.name, val))\n \n         elif self.type == STRING:\n             # Escape \\ and \"\n-            append_fn('CONFIG_{0}=\"{1}\"'\n-                      .format(self.name,\n+            append_fn('{0}{1}=\"{2}\"'\n+                      .format(self.config.config_prefix, self.name,\n                               val.replace(\"\\\\\", \"\\\\\\\\\").replace('\"', '\\\\\"')))\n \n         else:\n@@ -2635,7 +2686,7 @@ class Menu(Item):\n         self.title = None\n         self.dep_expr = None\n         self.visible_if_expr = None\n-        self.block = None\n+        self.block = [] # List of contained items\n         self.config = None\n         self.parent = None\n \n@@ -2852,7 +2903,7 @@ class Choice(Item):\n         self.prompts = []\n         self.def_exprs = [] # 'default' properties\n         self.help = None # Help text\n-        self.block = None # List of contained items\n+        self.block = [] # List of contained items\n         self.config = None\n         self.parent = None\n \n@@ -3177,7 +3228,13 @@ def _get_visibility(sc):\n             vis = sc.config._eval_max(vis, cond_expr)\n \n         if isinstance(sc, Symbol) and sc.is_choice_sym:\n-            vis = sc.config._eval_min(vis, _get_visibility(sc.parent))\n+            if sc.type == TRISTATE and vis == \"m\" and \\\n+               sc.parent.get_mode() == \"y\":\n+                # Choice symbols with visibility \"m\" are not visible if the\n+                # choice has mode \"y\"\n+                vis = \"n\"\n+            else:\n+                vis = sc.config._eval_min(vis, _get_visibility(sc.parent))\n \n         # Promote \"m\" to \"y\" if we're dealing with a non-tristate\n         if vis == \"m\" and sc.type != TRISTATE:\n@@ -3434,7 +3491,7 @@ _get_keyword = \\\n    \"prompt\": T_PROMPT, \"default\": T_DEFAULT, \"bool\": T_BOOL, \"boolean\": T_BOOL,\n    \"tristate\": T_TRISTATE, \"int\": T_INT, \"hex\": T_HEX, \"def_bool\": T_DEF_BOOL,\n    \"def_tristate\": T_DEF_TRISTATE, \"string\": T_STRING, \"select\": T_SELECT,\n-   \"imply\": T_IMPLY, \"range\": T_RANGE, \"option\": T_OPTION,\n+   \"imply\" : T_IMPLY, \"range\": T_RANGE, \"option\": T_OPTION,\n    \"allnoconfig_y\": T_ALLNOCONFIG_Y, \"env\": T_ENV,\n    \"defconfig_list\": T_DEFCONFIG_LIST, \"modules\": T_MODULES,\n    \"visible\": T_VISIBLE}.get\n@@ -3455,10 +3512,6 @@ _initial_token_re_match = re.compile(r\"[^\\w]*(\\w+)\\s*\").match\n # trailing whitespace as an optimization.\n _id_keyword_re_match = re.compile(r\"\\s*([\\w./-]+)\\s*\").match\n \n-# Regular expressions for parsing .config files\n-_set_re_match = re.compile(r\"CONFIG_(\\w+)=(.*)\").match\n-_unset_re_match = re.compile(r\"# CONFIG_(\\w+) is not set\").match\n-\n # Regular expression for finding $-references to symbols in strings\n _sym_ref_re_search = re.compile(r\"\\$[A-Za-z0-9_]+\").search\n \n",
    "prefixes": [
        "U-Boot",
        "1/1"
    ]
}