How to build wheels with different entry points and scripts

I’m working on a pure Python package that, for Technical Reasons™, needs to have one set of entry points on Unix systems, and a different set of entry points along with some helper scripts on Windows.

This would be easy enough to do if I just distributed an sdist with setuptools, but I also want to supply built wheels.

The solution I came up with has everything except the entry points specified in pyproject.toml, and this in my setup.py:

from setuptools import setup
from wheel.bdist_wheel import bdist_wheel as original_bdist_wheel


class bdist_wheel(original_bdist_wheel):
    def run(self):
        if self.plat_name.startswith("win"):
            self.distribution.entry_points["console_scripts"].remove(
                "cmd2 = my_pkg:cmd2"
            )
            self.distribution.entry_points["console_scripts"].append(
                "windows_cmd = my_pkg:windows_cmd"
            )
            self.distribution.scripts = ["scripts/script1.bat", "scripts/script2.bat"]

        super().run()


setup(
    cmdclass={"bdist_wheel": bdist_wheel},
    entry_points={
        "console_scripts": [
            "cmd1 = my_pkg:cmd1",
            "cmd2 = my_pkg:cmd2",
        ]
    },
)

Then to build my distributions, I can run one of these sets commands:

$ python setup.py sdist bdist_wheel
$ python setup.py bdist_wheel -p win32
$ python setup.py bdist_wheel -p win_amd64
$ python -m build
$ python -m build --wheel --config-setting=--build-option=--plat-name=win32
$ python -m build --wheel --config-setting=--build-option=--plat-name=win_amd64

Is this a good way to do this? Could it be better?

Never run these, they are deprecated.

Also, I don’t think it’s right to pass the platform as --config-setting. If your users install the sdist you distribute on PyPI, the config setting won’t be provided to pip. Instead, you should simply check the current running platform (import platform then platform.platform()). The primary use case for platform-dependent wheels is native code compilation, and cross-compilation is really hard, so the existing ecosystem mostly assumes that a wheel for a given platform is always built on that platform.

“Technical Reasons™” I’m guessing you mean an imposed solution…

What are you doing that requires these helper scripts on windows but not unix?
Can’t you do what ever the batch files do from within your python code?

Assuming that this must be solved and you want unix wheels and windows wheels that are different you could structure things into:

  • common code in a wheel
  • unix code that depends on the common code in a wheel
  • windows code that depends on the command code ina wheel

Do the entry points actually need to be named differently on each system?

Why can’t the code at the entry point do the OS detection and delegate as needed?

1 Like

Mmm, yeah you’re right. When building on Windows, the scripts are properly included in the wheel, but the wheel is still tagged as any. Definitely going to need a different approach.