Repository: pypa/cibuildwheel

Issue:
Idea: Arch 32/64 shortcut
When looking at the build matrix here:

https://github.com/deepcharles/ruptures/blob/404957563c0391f073200899b9b041caa9277cc6/.github/workflows/upload-to-pypi.yml#L40-L80

It seems we could have shortcut values for `ARCHS`, "32" and "64", that would select 32 bit and 64 bit native architectures. So on 64 bit Windows or Linux, you could select 32 or 64. On macOS, you could only select 64, 32 would be empty (so an error after #545 unless `--allow-empty` was passed) or we could just make it an error. Same thing for 64 on 32bit OS's. On alternate archs, these remain the "native" on that arch 32/64 values.

I believe @mayeut wanted something like this too? Don't have the issue reference handy for it.

This would take a lot of pressure off the build selector. ARCHS, like BUILD/SKIP, already helps define the build process.

<details><summary>Old version</summary>

```yaml
matrix:
  os: [ubuntu-latest, windows-latest, macos-latest]
  bitness: [32, 64]
  python: [36, 37, 38, 39]
  include:
    # Run 32 and 64 bits version in parallel for Linux and Windows
    - os: windows-latest
      bitness: 64
      platform_id: win_amd64
    - os: windows-latest
      bitness: 32
      platform_id: win32
    - os: ubuntu-latest
      bitness: 64
      platform_id: manylinux_x86_64
    - os: ubuntu-latest
      bitness: 32
      platform_id: manylinux_i686
    - os: macos-latest
      bitness: 64
      platform_id: macosx_x86_64
  exclude:
    - os: macos-latest
      bitness: 32
    - os: windows-latest
      python: 39
steps:
- uses: actions/checkout@v2
  with:
    fetch-depth: 0
- uses: actions/setup-python@v2
  name: Install Python
  with:
    python-version: '3.8'
- name: Install cibuildwheel
  run: |
    python -m pip install --upgrade pip
    python -m pip install cibuildwheel
- name: Build wheels
  env:
    CIBW_BUILD: cp${{ matrix.python }}-${{ matrix.platform_id }}
    run: python -m cibuildwheel --output-dir wheelhouse
```

</details>

<details><summary>New version</summary>

```yaml
matrix:
  os: [ubuntu-latest, windows-latest, macos-latest]
  bitness: [32, 64]
  python: [36, 37, 38, 39]
  exclude:
    - os: macos-latest
      bitness: 32
    - os: windows-latest
      python: 39
steps:
- uses: actions/checkout@v2
  with:
    fetch-depth: 0
- name: Build wheels
  env:
    CIBW_BUILD: cp${{ matrix.python }}-*
  run: pipx run cibuildwheel==1.80 --output-dir wheelhouse --archs ${{ matrix.bitness }}
```

</details>

Thoughts? Hopefully @deepcharles doesn't mind me using his yaml as an example. :) Also used pipx for the "new" version, because why not, it's preinstalled. :)


Discussion:
I didn't know you could do a partial exclude like that, by the way, nice. :)
all credit goes to @oboulant on that one
Maybe `native64`/`native32`, then? Just `32` or `64` seems a bit vague.
But then we have the weird issue that 32 isn't considered `native` on a 64-bit version of Linux, for example?
I don't think `--archs 32` or `CIBW_ARCHS: "32"` is at all vague when you are reading a config. Though I thought about suggesting `native32` or `native_32`, something like that. I guess we could do `auto32`, to indicate that it's the same as auto but only the 32 bit part, but I think just `32` is fine. Though `auto32`/`auto64` doesn't look that bad...
`32bit`/`64bit`, possibly?

`32`/`64` is my favorite, with `auto32`/`auto64` a very close second.

Resolving patch:
diff --git a/cibuildwheel/__main__.py b/cibuildwheel/__main__.py
--- a/cibuildwheel/__main__.py
+++ b/cibuildwheel/__main__.py
@@ -80,7 +80,7 @@ def main() -> None:
                             on this machine. Set this option to build an architecture
                             via emulation, for example, using binfmt_misc and QEMU.
                             Default: auto.
-                            Choices: auto, native, all, {}
+                            Choices: auto, auto64, auto32, native, all, {}
                         '''.format(", ".join(a.name for a in Architecture)))
     parser.add_argument('--output-dir',
                         default=os.environ.get('CIBW_OUTPUT_DIR', 'wheelhouse'),
diff --git a/cibuildwheel/architecture.py b/cibuildwheel/architecture.py
--- a/cibuildwheel/architecture.py
+++ b/cibuildwheel/architecture.py
@@ -4,7 +4,7 @@
 from enum import Enum
 from typing import Set
 
-from .typing import PlatformName, assert_never
+from .typing import Literal, PlatformName, assert_never
 
 PRETTY_NAMES = {'linux': 'Linux', 'macos': 'macOS', 'windows': 'Windows'}
 
@@ -44,6 +44,10 @@ def parse_config(config: str, platform: PlatformName) -> 'Set[Architecture]':
                 result.add(Architecture(platform_module.machine()))
             elif arch_str == 'all':
                 result |= Architecture.all_archs(platform=platform)
+            elif arch_str == 'auto64':
+                result |= Architecture.bitness_archs(platform=platform, bitness="64")
+            elif arch_str == 'auto32':
+                result |= Architecture.bitness_archs(platform=platform, bitness="32")
             else:
                 result.add(Architecture(arch_str))
         return result
@@ -77,6 +81,18 @@ def all_archs(platform: PlatformName) -> 'Set[Architecture]':
         else:
             assert_never(platform)
 
+    @staticmethod
+    def bitness_archs(platform: PlatformName, bitness: Literal['64', '32']) -> 'Set[Architecture]':
+        archs_32 = {Architecture.i686, Architecture.x86}
+        auto_archs = Architecture.auto_archs(platform)
+
+        if bitness == '64':
+            return auto_archs - archs_32
+        elif bitness == '32':
+            return auto_archs & archs_32
+        else:
+            assert_never(bitness)
+
 
 def allowed_architectures_check(
     platform: PlatformName,


Test patch:
diff --git a/unit_test/main_tests/main_platform_test.py b/unit_test/main_tests/main_platform_test.py
--- a/unit_test/main_tests/main_platform_test.py
+++ b/unit_test/main_tests/main_platform_test.py
@@ -131,6 +131,39 @@ def test_archs_platform_native(platform, intercepted_build_args, monkeypatch):
         assert build_options.architectures == {Architecture.x86_64}
 
 
+def test_archs_platform_auto64(platform, intercepted_build_args, monkeypatch):
+    monkeypatch.setenv('CIBW_ARCHS', 'auto64')
+
+    main()
+    build_options = intercepted_build_args.args[0]
+
+    if platform == 'linux':
+        assert build_options.architectures == {Architecture.x86_64}
+    elif platform == 'windows':
+        assert build_options.architectures == {Architecture.AMD64}
+    elif platform == 'macos':
+        assert build_options.architectures == {Architecture.x86_64}
+
+
+def test_archs_platform_auto32(platform, intercepted_build_args, monkeypatch):
+    monkeypatch.setenv('CIBW_ARCHS', 'auto32')
+
+    if platform == 'macos':
+        with pytest.raises(SystemExit) as exit:
+            main()
+        assert exit.value.args == (4,)
+
+    else:
+        main()
+
+        build_options = intercepted_build_args.args[0]
+
+        if platform == 'linux':
+            assert build_options.architectures == {Architecture.i686}
+        elif platform == 'windows':
+            assert build_options.architectures == {Architecture.x86}
+
+
 def test_archs_platform_all(platform, intercepted_build_args, monkeypatch):
     monkeypatch.setenv('CIBW_ARCHS', 'all')