linux-arm64 python binary support (#669)

* add aarch64-unknown-linux-gnu python binary release
* unittests run & pass on my m1 macbook in OSX and Linux
    * Note: most //examples/... tests fail when run due to lack of support
      for non-x86 in https://github.com/bazelbuild/bazel-integration-testing/,
      but those failing tests pass when run directly in the directory with
      bazel

Co-authored-by: Thulio Ferraz Assis <3149049+f0rmiga@users.noreply.github.com>
diff --git a/examples/wheel/BUILD b/examples/wheel/BUILD
index 151a9c9..f745dc3 100644
--- a/examples/wheel/BUILD
+++ b/examples/wheel/BUILD
@@ -14,6 +14,7 @@
 
 load("//python:defs.bzl", "py_library", "py_test")
 load("//python:packaging.bzl", "py_package", "py_wheel")
+load("//python:versions.bzl", "gen_python_config_settings")
 
 package(default_visibility = ["//visibility:public"])
 
@@ -196,11 +197,20 @@
     ],
 )
 
+gen_python_config_settings()
+
 py_wheel(
     name = "python_abi3_binary_wheel",
     abi = "abi3",
     distribution = "example_python_abi3_binary_wheel",
-    platform = "manylinux2014_x86_64",
+    # these platform strings must line up with test_python_abi3_binary_wheel() in wheel_test.py
+    platform = select({
+        ":aarch64-apple-darwin": "macosx_11_0_arm64",
+        ":aarch64-unknown-linux-gnu": "manylinux2014_aarch64",
+        ":x86_64-apple-darwin": "macosx_11_0_x86_64",  # this is typically macosx_10_9_x86_64?
+        ":x86_64-pc-windows-msvc": "win_amd64",
+        ":x86_64-unknown-linux-gnu": "manylinux2014_x86_64",
+    }),
     python_requires = ">=3.8",
     python_tag = "cp38",
     version = "0.0.1",
diff --git a/examples/wheel/wheel_test.py b/examples/wheel/wheel_test.py
index be74792..f326af2 100644
--- a/examples/wheel/wheel_test.py
+++ b/examples/wheel/wheel_test.py
@@ -14,6 +14,7 @@
 
 import os
 import platform
+import subprocess
 import unittest
 import zipfile
 
@@ -309,12 +310,22 @@
             )
 
     def test_python_abi3_binary_wheel(self):
+        arch = "amd64"
+        if platform.system() != "Windows":
+            arch = subprocess.check_output(["uname", "-m"]).strip().decode()
+        # These strings match the strings from py_wheel() in BUILD
+        os_strings = {
+            "Linux": "manylinux2014",
+            "Darwin": "macosx_11_0",
+            "Windows": "win",
+        }
+        os_string = os_strings[platform.system()]
         filename = os.path.join(
             os.environ["TEST_SRCDIR"],
             "rules_python",
             "examples",
             "wheel",
-            "example_python_abi3_binary_wheel-0.0.1-cp38-abi3-manylinux2014_x86_64.whl",
+            f"example_python_abi3_binary_wheel-0.0.1-cp38-abi3-{os_string}_{arch}.whl",
         )
         with zipfile.ZipFile(filename) as zf:
             metadata_contents = zf.read(
@@ -336,12 +347,12 @@
                 "example_python_abi3_binary_wheel-0.0.1.dist-info/WHEEL"
             )
             self.assertEqual(
-                wheel_contents,
-                b"""\
+                wheel_contents.decode(),
+                f"""\
 Wheel-Version: 1.0
 Generator: bazel-wheelmaker 1.0
 Root-Is-Purelib: false
-Tag: cp38-abi3-manylinux2014_x86_64
+Tag: cp38-abi3-{os_string}_{arch}
 """,
             )
 
diff --git a/python/versions.bzl b/python/versions.bzl
index ba1ce5e..b263abf 100644
--- a/python/versions.bzl
+++ b/python/versions.bzl
@@ -40,6 +40,7 @@
         "url": "20220227/cpython-{python_version}+20220227-{platform}-{build}.tar.gz",
         "sha256": {
             "aarch64-apple-darwin": "f9a3cbb81e0463d6615125964762d133387d561b226a30199f5b039b20f1d944",
+            # no aarch64-unknown-linux-gnu build available for 3.8.12
             "x86_64-apple-darwin": "f323fbc558035c13a85ce2267d0fad9e89282268ecb810e364fff1d0a079d525",
             "x86_64-pc-windows-msvc": "924f9fd51ff6ccc533ed8e96c5461768da5781eb3dfc11d846f9e300fab44eda",
             "x86_64-unknown-linux-gnu": "5be9c6d61e238b90dfd94755051c0d3a2d8023ebffdb4b0fa4e8fedd09a6cab6",
@@ -50,6 +51,7 @@
         "url": "20220227/cpython-{python_version}+20220227-{platform}-{build}.tar.gz",
         "sha256": {
             "aarch64-apple-darwin": "ad66c2a3e7263147e046a32694de7b897a46fb0124409d29d3a93ede631c8aee",
+            "aarch64-unknown-linux-gnu": "12dd1f125762f47975990ec744532a1cf3db74ad60f4dfb476ca42deb7f78ca4",
             "x86_64-apple-darwin": "fdaf594142446029e314a9beb91f1ac75af866320b50b8b968181e592550cd68",
             "x86_64-pc-windows-msvc": "5bc65ce023614bf496a6748e41dca934b70fc5fac6dfacc46aa8dbcad772afc2",
             "x86_64-unknown-linux-gnu": "455089cc576bd9a58db45e919d1fc867ecdbb0208067dffc845cc9bbf0701b70",
@@ -60,6 +62,7 @@
         "url": "20220227/cpython-{python_version}+20220227-{platform}-{build}.tar.gz",
         "sha256": {
             "aarch64-apple-darwin": "1409acd9a506e2d1d3b65c1488db4e40d8f19d09a7df099667c87a506f71c0ef",
+            "aarch64-unknown-linux-gnu": "8f351a8cc348bb45c0f95b8634c8345ec6e749e483384188ad865b7428342703",
             "x86_64-apple-darwin": "8146ad4390710ec69b316a5649912df0247d35f4a42e2aa9615bffd87b3e235a",
             "x86_64-pc-windows-msvc": "a293c5838dd9c8438a84372fb95dda9752df63928a8a2ae516438f187f89567d",
             "x86_64-unknown-linux-gnu": "9b64eca2a94f7aff9409ad70bdaa7fbbf8148692662e764401883957943620dd",
@@ -86,6 +89,17 @@
         # repository_ctx.execute(["uname", "-m"]).stdout.strip()
         arch = "arm64",
     ),
+    "aarch64-unknown-linux-gnu": struct(
+        compatible_with = [
+            "@platforms//os:linux",
+            "@platforms//cpu:aarch64",
+        ],
+        os_name = LINUX_NAME,
+        # Note: this string differs between OSX and Linux
+        # Matches the value returned from:
+        # repository_ctx.execute(["uname", "-m"]).stdout.strip()
+        arch = "aarch64",
+    ),
     "x86_64-apple-darwin": struct(
         compatible_with = [
             "@platforms//os:macos",
@@ -176,3 +190,10 @@
         )
         for platform in TOOL_VERSIONS[python_version]["sha256"].keys()
     ])
+
+def gen_python_config_settings(name = ""):
+    for platform in PLATFORMS.keys():
+        native.config_setting(
+            name = "{name}{platform}".format(name = name, platform = platform),
+            constraint_values = PLATFORMS[platform].compatible_with,
+        )