Read Part 1 here.

Coming from macOS into Ubuntu required some “getting used to things”. Like using apt (and nala), but still I installed Homebrew for Linux, as most of the utilities are not up to date, and being more used to Homebrew it only made sense to me.

So, as usual, I started setting up my development environment, which requires Python 2.7 and Python 3.10, and for managing multiple versions of Python pyenv is my go-to over similar tools like asdf.

Installing Python 2.7 was a breeze, but I did run into some issues when installing Python 3.10 mainly because pyenv’s python-build script relies in Homebrew only when running on macOS, which might be an opportunity for creating a feature request.

Even after getting most of the errors listed in pyenv’s “Common build problems” wiki page it dawned upon me that I was approaching this the wrong way. If pyenv’s python-build script can’t do it, I can build Python following Python’s guide!

Although I did notice some issues with the instructions in there, and so PR python/devguide#1052 was created.

And this is how one can build Python in Ubuntu and macOS with Homebrew.

Using Python’s Setup and Building, and the script I generated using Build, compile and install Python from source code I had success doing the following.

I’m sure that seasoned Linux and Ubuntu users might not even consider Homebrew as an alternative to their built-in package manager, but brew is what I know.

First install all required packages with brew:

brew install pkg-config openssl@1.1 xz gdbm tcl-tk wget pyenv

And for building Python 3.10:

$ cd /tmp/
$ wget https://www.python.org/ftp/python/3.10.12/Python-3.10.12.tgz
$ tar xzf Python-3.10.12.tgz
$ cd Python-3.10.12
$ CPPFLAGS="-I$(brew --prefix gdbm)/include -I$(brew --prefix xz)/include" \
  LDFLAGS="-L$(brew --prefix gdbm)/lib -L$(brew --prefix xz)/lib" \
  ./configure --with-openssl="$(brew --prefix openssl@3.0)" \
              --with-tcltk-libs="$(pkg-config --libs tcl tk)" \
              --with-tcltk-includes="$(pkg-config --cflags tcl tk)" \
              --prefix="${PYENV_ROOT}/versions/3.10.12/"
$ make -s -j2
 CC='gcc' LDSHARED='gcc -shared -L/home/linuxbrew/.linuxbrew/opt/gdbm/lib -L/home/linuxbrew/.linuxbrew/opt/xz/lib   ' OPT='-g -Og -Wall'      _TCLTK_INCLUDES='-I/home/linuxbrew/.linuxbrew/Cellar/tcl-tk/8.6.13_3/include/tcl-tk -I/home/linuxbrew/.linuxbrew/opt/zlib/include' _TCLTK_LIBS='-L/home/linuxbrew/.linuxbrew/Cellar/tcl-tk/8.6.13_3/lib -ltk8.6 -ltkstub8.6 -ltcl8.6 -ltclstub8.6'  ./python -E ./setup.py -q build

The following modules found by detect_modules() in setup.py, have been
built by the Makefile instead, as configured by the Setup files:
_abc                  pwd                   time

renaming build/scripts-3.10/pydoc3 to build/scripts-3.10/pydoc3.10
renaming build/scripts-3.10/idle3 to build/scripts-3.10/idle3.10
renaming build/scripts-3.10/2to3 to build/scripts-3.10/2to3-3.10
$ make altinstall

Finally add symlinks:

ln -s ${PYENV_ROOT}/versions/3.10.12/bin/2to3-3.10            ${PYENV_ROOT}/versions/3.10.12/bin/2to3
ln -s ${PYENV_ROOT}/versions/3.10.12/bin/idle3.10             ${PYENV_ROOT}/versions/3.10.12/bin/idle
ln -s ${PYENV_ROOT}/versions/3.10.12/bin/pip3.10              ${PYENV_ROOT}/versions/3.10.12/bin/pip
ln -s ${PYENV_ROOT}/versions/3.10.12/bin/pip3.10              ${PYENV_ROOT}/versions/3.10.12/bin/pip3
ln -s ${PYENV_ROOT}/versions/3.10.12/bin/pydoc3.10            ${PYENV_ROOT}/versions/3.10.12/bin/pydoc
ln -s ${PYENV_ROOT}/versions/3.10.12/bin/python3.10           ${PYENV_ROOT}/versions/3.10.12/bin/python
ln -s ${PYENV_ROOT}/versions/3.10.12/bin/python3.10-config    ${PYENV_ROOT}/versions/3.10.12/bin/python-config
ln -s ${PYENV_ROOT}/versions/3.10.12/bin/python3.10           ${PYENV_ROOT}/versions/3.10.12/bin/python3

And just like that I was ready to go.

Thanks for reading, and happy building!