Repository: pypa/auditwheel

Issue:
ncurses 5 -> 6 transition means we probably need to drop some libraries from the manylinux whitelist
The [PEP 513 whitelist](https://www.python.org/dev/peps/pep-0513/#the-manylinux1-policy) includes `libpanelw.so.5` and `libncursesw.so.5`, both of which are part of ncurses version 5.

But apparently the new hostness is [ncurses 6, which breaks ABI with ncurses 5](https://www.gnu.org/software/ncurses/). I'm told that Fedora 24 and successors have already switched to ncurses 6, with the ncurses 5 libraries being available in a compatibility package, but not installed by default. So... I guess these have to be dropped from the whitelist.

Comments:

Patch:
diff --git a/auditwheel/policy/__init__.py b/auditwheel/policy/__init__.py
index 22879c0..a9d59d0 100644
--- a/auditwheel/policy/__init__.py
+++ b/auditwheel/policy/__init__.py
@@ -1,7 +1,8 @@
 import sys
 import json
 import platform as _platform_module
-from typing import Optional, List
+from collections import defaultdict
+from typing import Dict, List, Optional, Set
 from os.path import join, dirname, abspath
 import logging
 
@@ -23,9 +24,42 @@ def get_arch_name() -> str:
 _ARCH_NAME = get_arch_name()
 
 
+def _validate_pep600_compliance(policies) -> None:
+    symbol_versions: Dict[str, Dict[str, Set[str]]] = {}
+    lib_whitelist: Set[str] = set()
+    for policy in sorted(policies, key=lambda x: x['priority'], reverse=True):
+        if policy['name'] == 'linux':
+            continue
+        if not lib_whitelist.issubset(set(policy['lib_whitelist'])):
+            diff = lib_whitelist - set(policy["lib_whitelist"])
+            raise ValueError(
+                'Invalid "policy.json" file. Missing whitelist libraries in '
+                f'"{policy["name"]}" compared to previous policies: {diff}'
+            )
+        lib_whitelist.update(policy['lib_whitelist'])
+        for arch in policy['symbol_versions'].keys():
+            symbol_versions_arch = symbol_versions.get(arch, defaultdict(set))
+            for prefix in policy['symbol_versions'][arch].keys():
+                policy_symbol_versions = set(
+                    policy['symbol_versions'][arch][prefix])
+                if not symbol_versions_arch[prefix].issubset(
+                        policy_symbol_versions):
+                    diff = symbol_versions_arch[prefix] - \
+                           policy_symbol_versions
+                    raise ValueError(
+                        'Invalid "policy.json" file. Symbol versions missing '
+                        f'in "{policy["name"]}_{arch}" for "{prefix}" '
+                        f'compared to previous policies: {diff}'
+                    )
+                symbol_versions_arch[prefix].update(
+                    policy['symbol_versions'][arch][prefix])
+            symbol_versions[arch] = symbol_versions_arch
+
+
 with open(join(dirname(abspath(__file__)), 'policy.json')) as f:
     _POLICIES = []
     _policies_temp = json.load(f)
+    _validate_pep600_compliance(_policies_temp)
     for _p in _policies_temp:
         if _ARCH_NAME in _p['symbol_versions'].keys() or _p['name'] == 'linux':
             if _p['name'] != 'linux':
diff --git a/auditwheel/policy/policy.json b/auditwheel/policy/policy.json
index 7c9b722..bce6fb9 100644
--- a/auditwheel/policy/policy.json
+++ b/auditwheel/policy/policy.json
@@ -9,8 +9,8 @@
      "symbol_versions": {
          "i686": {
            "CXXABI": ["1.3", "1.3.1"],
-           "GCC": ["3.0", "3.3", "3.3.1", "3.4", "3.4.2", "3.4.4", "4.0.0", "4.2.0"],
-           "GLIBC": ["2.0", "2.1", "2.1.1", "2.1.2", "2.1.3", "2.2", "2.2.1", "2.2.2", "2.2.3", "2.2.4", "2.2.5", "2.2.6", "2.3", "2.3.2", "2.3.3", "2.3.4", "2.4", "2.5"],
+           "GCC": ["3.0", "3.3", "3.3.1", "3.4", "3.4.2", "4.0.0", "4.2.0"],
+           "GLIBC": ["2.0", "2.1", "2.1.1", "2.1.2", "2.1.3", "2.2", "2.2.1", "2.2.2", "2.2.3", "2.2.4", "2.2.6", "2.3", "2.3.2", "2.3.3", "2.3.4", "2.4", "2.5"],
            "GLIBCXX": ["3.4", "3.4.1", "3.4.2", "3.4.3", "3.4.4", "3.4.5", "3.4.6", "3.4.7", "3.4.8"]
          },
          "x86_64": {
@@ -21,7 +21,6 @@
          }
      },
      "lib_whitelist": [
-         "libpanelw.so.5", "libncursesw.so.5",
          "libgcc_s.so.1",
          "libstdc++.so.6",
          "libm.so.6", "libdl.so.2", "librt.so.1",


Test patch:
diff --git a/tests/unit/test_policy.py b/tests/unit/test_policy.py
index 8a512e0..c926929 100644
--- a/tests/unit/test_policy.py
+++ b/tests/unit/test_policy.py
@@ -3,7 +3,7 @@ from unittest.mock import patch
 import pytest
 
 from auditwheel.policy import get_arch_name, get_policy_name, \
-    get_priority_by_name, get_replace_platforms
+    get_priority_by_name, get_replace_platforms, _validate_pep600_compliance
 
 
 @patch("auditwheel.policy._platform_module.machine")
@@ -43,6 +43,59 @@ def test_replacement_platform(name, expected):
     assert get_replace_platforms(name) == expected
 
 
+def test_pep600_compliance():
+    _validate_pep600_compliance([{
+        "name": "manylinux1", "priority": 100, "symbol_versions": {
+            "i686": {"CXXABI": ["1.3"]},
+        },
+        "lib_whitelist": ["libgcc_s.so.1"]
+    }, {
+        "name": "manylinux2010", "priority": 90, "symbol_versions": {
+            "i686": {"CXXABI": ["1.3", "1.3.1"]},
+        },
+        "lib_whitelist": ["libgcc_s.so.1", "libstdc++.so.6"],
+    }])
+
+    _validate_pep600_compliance([{
+        "name": "manylinux1", "priority": 100, "symbol_versions": {
+            "i686": {"CXXABI": ["1.3"]},
+            "x86_64": {"CXXABI": ["1.3"]},
+        },
+        "lib_whitelist": ["libgcc_s.so.1"]
+    }, {
+        "name": "manylinux2010", "priority": 90, "symbol_versions": {
+            "i686": {"CXXABI": ["1.3", "1.3.1"]},
+        },
+        "lib_whitelist": ["libgcc_s.so.1", "libstdc++.so.6"],
+    }])
+
+    with pytest.raises(ValueError, match="manylinux2010_i686.*CXXABI.*1.3.2"):
+        _validate_pep600_compliance([{
+            "name": "manylinux1", "priority": 100, "symbol_versions": {
+                "i686": {"CXXABI": ["1.3", "1.3.2"]},
+            },
+            "lib_whitelist": ["libgcc_s.so.1"]
+        }, {
+            "name": "manylinux2010", "priority": 90, "symbol_versions": {
+                "i686": {"CXXABI": ["1.3", "1.3.1"]},
+            },
+            "lib_whitelist": ["libgcc_s.so.1", "libstdc++.so.6"],
+        }])
+
+    with pytest.raises(ValueError, match="manylinux2010.*libstdc\+\+\.so\.6"):
+        _validate_pep600_compliance([{
+            "name": "manylinux1", "priority": 100, "symbol_versions": {
+                "i686": {"CXXABI": ["1.3"]},
+            },
+            "lib_whitelist": ["libgcc_s.so.1", "libstdc++.so.6"]
+        }, {
+            "name": "manylinux2010", "priority": 90, "symbol_versions": {
+                "i686": {"CXXABI": ["1.3", "1.3.1"]},
+            },
+            "lib_whitelist": ["libgcc_s.so.1"],
+        }])
+
+
 class TestPolicyAccess:
 
     def test_get_by_priority(self):