Bazel, as at 6.0.0, still relies on, by default, the system python installation.
This is non-hermetic and problematic when you need to be specific about python versions; i.e. which 3.x?
Happily the python standalone releases can be added to a --distdir
There's only a handful of python versions hardcoded into @rules_python//python:versions.bzl
. So one would have to DIY
the configuration for apparently esoteric versions.
One might want to DIY it anyway and make use of your own platforms and constraints to automatically select the toolchain(s).
Building from source is problematic as the build system may have different library versions linked; e.g. openssl, zlib.
There are a couple of approaches others have taken to solve this:
Using some form of portable python is maddeningly recursive as it's written in python.
Python 3.6.15 has been DIY'd with rules_foreign_cc warts 'n' all.
- You'll need the python source for which ever version you want, this example uses 3.6.x as that happens to be the version available in crappy old EoL'd RHEL6 which just might still be hanging around smelling up the place.
- You'll also need sources for some python modules' dependencies: this example uses zlib, bzip2, and openssl as you'll need
these to use
pip
. - Load these in
WORKSPACE
withhttp_archive
, build them usingrules_foreign_cc
. - Define a
py_runtime_pair
toolchain and use it.
bz2_dirs=($(bazel cquery --output files @bzip2-1.0.8//:dir))
ssl_dirs=($(bazel cquery --output files @openssl-1.1.1w//:dir))
py3_dirs=($(bazel cquery --output files @python-3.6.15//:dir))
# What do they need?
objdump -p ${py3_dirs[0]}/lib/python3.6/lib-dynload/_bz2.cpython-36m-x86_64-linux-gnu.so | grep NEEDED
objdump -p ${py3_dirs[0]}/lib/python3.6/lib-dynload/_ssl.cpython-36m-x86_64-linux-gnu.so | grep NEEDED
# Where can they be found?
export LD_LIBRARY_PATH="${bz2_dirs[@]/%//lib:}${ssl_dirs[@]/%//lib:}${LD_LIBRARY_PATH}"
ldd ${py3_dirs[0]}/lib/python3.6/lib-dynload/_bz2.cpython-36m-x86_64-linux-gnu.so
ldd ${py3_dirs[0]}/lib/python3.6/lib-dynload/_ssl.cpython-36m-x86_64-linux-gnu.so
Notice (above) we have to configure LD_LIBRARY_PATH
manually, the same applies to genrule
and py_binary
rules.
Dynamic linking is fiddly. Configuring python to static link is also fiddly, if this works at all:
genrule(
name = "local", # ... and include in :srcs
outs = ["python36/Modules/Setup.local"], # path prefix includes the value of configure_make(lib_name)
cmd = """
exec >> $@
# Copied from Modules/Setup.dist and modified
echo 'SSL=$(location @openssl//:dir)'
echo '_ssl _ssl.c -DUSE_SSL -I$$(SSL)/include -I$$(SSL)/include/openssl $$(SSL)/lib/libssl.a $$(SSL)/lib/libcrypto.a # statically link in libssl and libcrypto'
""",
tools = [
"@openssl//:dir",
],
)
To run these examples:
bazel test --config py36 ...
bazel test --config py39 ...
bazel test --config py311 ...
bazel test ...
bazel run --config py36 hello # > Hello, 3.6.15! # this is our DIY built toolchain.
bazel run --config py39 hello # > Hello, 3.9.12!
bazel run --config py311 hello # > Hello, 3.11.1!
bazel run hello # > Hello, 3.10.7! # this is the OS's python version.
bazel run --config py36 hello_ssl # > Hello SSL, OpenSSL 1.1.1t 7 Feb 2023! # this is our DIY built toolchain.
bazel run --config py39 hello_ssl # > Hello SSL, OpenSSL 1.1.1n 15 Mar 2022!
bazel run --config py311 hello_ssl # > Hello SSL, OpenSSL 1.1.1s 1 Nov 2022!
bazel run hello_ssl # > Hello SSL, OpenSSL 3.0.5 5 Jul 2022! # this is the OS's openssl version.