Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/1.2/patches/815413/?format=api
{ "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" ] }