This is the solution I am currently using for pypdfium2:
- Create a class of supported platforms whose values correspond to the data directory names:
class PlatformNames:
darwin_x64 = "darwin_x64"
linux_x64 = "linux_x64"
windows_x64 = "windows_x64"
# ...
sourcebuild = "sourcebuild"
- Wrap
setuptools.setup() with a function that takes the platform name as argument and copies platform-dependent files into the source tree as required:
# A list of non-python file names to consider for inclusion in the installation, e. g.
Libnames = (
"somelib.so",
"somelib.dll",
"somelib.dylib",
)
# _clean() removes possible old binaries/bindings
# _copy_bindings() copies the new stuff into the source tree
# _get_bdist() returns a custom `wheel.bdist_wheel` subclass with the `get_tag()` and `finalize_options()` functions overridden so as to tag the wheels according to their target platform.
def mkwheel(pl_name):
_clean()
_copy_bindings(pl_name)
setuptools.setup(
package_data = {"": Libnames},
cmdclass = {"bdist_wheel": _get_bdist(pl_name)},
# ...
)
# not cleaning up afterwards so that editable installs work (`pip3 install -e .`)
- In
setup.py, query for a custom environment variable defining the target platform (e. g. $PYP_TARGET_PLATFORM).
- If set to a value that indicates the need for a source distribution (e. g.
sdist), run the raw setuptools.setup() function without copying in any build artifacts.
- If set to a platform name, build for the requested platform. This makes packaging platform-independent and avoids the need for native hosts to craft the wheels.
- If not set, detect the host platform using
sysconfig.get_platform() and call mkwheel() with the corresponding PlatformNames member.
- In case the detected platform is not supported, trigger code that performs a source build, moves the created files into
data/sourcebuild/ and runs mkwheel(PlatformNames.sourcebuild).
- Write a script that iterates through the platform names, sets your environment variable and runs
python3 -m build --no-isolation --skip-dependency-check --wheel for each. Also invoke build once with --sdist instead of --wheel and the environment variable set to the value for source distribution.
→ If all goes well, the platform-specific wheels and a source distribution will be written into dist/.
Perhaps this is a lot easier to understand just by looking at pypdfium2's code (especially setup.py, setup_base.py and craft_packages.py).
Disclaimer: I am not experienced with the setup infrastructure of Python and merely wrote this code out of personal need. I acknowledge that the approach is a bit "hacky". If there is a possibility to achieve the same goal while using the setuptools API in a more official sort of way, I'd be interested to hear about it.
Update 1: A negative implication of this concept is that the content wrongly ends up in a purelib folder, although it should be platlib as per PEP 427. I'm not sure how to instruct wheel/setuptools differently. Luckily, this is rather just a cosmetic problem.
Update 2: Found a fix to the purelib problem:
class BinaryDistribution (setuptools.Distribution):
def has_ext_modules(self):
return True
setuptools.setup(
# ...
distclass = BinaryDistribution,
)