Estos días he encontrado un extraño error a la hora de instalar MkDocs, pero por lo que parece, no es un problema específico ni de MkDocs ni de Alpine…

TL;DR;

Al final del artículo resumo los paquetes y módulos a instalar como requerimientos para la instalación de MkDocs en Alpine Linux y Mac OS Catalina.

Error en la instalación

Al intentar instalar MkDocs, aparece un mensaje de error indicando:

...
Running setup.py install for regex ... error
    ERROR: Command errored out with exit status 1:
     command: /usr/bin/python3 -u -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'/tmp/pip-install-9nfj6k97/regex/setup.py'"'"'; __file__='"'"'/tmp/pip-install-9nfj6k97/regex/setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(__file__);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' install --record /tmp/pip-record-_5wq4jam/install-record.txt --single-version-externally-managed --compile --install-headers /usr/include/python3.8/regex
         cwd: /tmp/pip-install-9nfj6k97/regex/

Al parecer, la instalación del paquete regex requiere compilar algo y a partir de aquí empiezan los problemas.

He creado una máquina virtual con Alpine 3.12 y he realizado pruebas hasta solucionar el problema; ahora, he creado una nueva VM para documentar cuál de todas las cosas que he probado es la solución que hay que aplicar.

Realizo una instalación de Alpine 3.12 con la mayor parte de los valores por defecto, despincho la ISO y reinicio.

Cambio la conectividad de red de la VM a Bridged y habilito la conexión vía SSH del usuario root (para poder copiar y pegar en la terminal).

# cat /etc/os-release
NAME="Alpine Linux"
ID=alpine
VERSION_ID=3.12.0
PRETTY_NAME="Alpine Linux v3.12"
HOME_URL="https://alpinelinux.org/"
BUG_REPORT_URL="https://bugs.alpinelinux.org/"

Comprobamos que en la instalación por defecto no tenemos ninguna versión de Python:

vm01:~# which python
vm01:~# python --version
-ash: python: not found
vm01:~# python3 --version
-ash: python3: not found

Instalamos Python 3:

# apk add python3
(1/7) Installing libbz2 (1.0.8-r1)
(2/7) Installing expat (2.2.9-r1)
(3/7) Installing libffi (3.3-r2)
(4/7) Installing gdbm (1.13-r1)
(5/7) Installing readline (8.0.4-r0)
(6/7) Installing sqlite-libs (3.32.1-r0)
(7/7) Installing python3 (3.8.5-r0)
Executing busybox-1.31.1-r19.trigger
OK: 863 MiB in 145 packages

Instalamos pip:

vm01:~# apk add py3-pip
ERROR: unsatisfiable constraints:
  py3-pip (missing):
    required by: world[py3-pip]

Como podemos comprobar en py3-pip, el paquete se encuentra en el repositorio world.

Lo habilitamos eliminando # de la línea http://dl-cdn.alpinelinux.org/alpine/edge/community en el fichero:

vm01:~# vi /etc/apk/repositories

Actualizamos e instalamos py3-pip:

# apk update
fetch http://dl-cdn.alpinelinux.org/alpine/v3.12/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/edge/community/x86_64/APKINDEX.tar.gz
v3.12.0-401-g86b6908751 [http://dl-cdn.alpinelinux.org/alpine/v3.12/main]
v20200917-2188-g82e8db59ac [http://dl-cdn.alpinelinux.org/alpine/edge/community]
OK: 13242 distinct packages available
vm01:~# apk add py3-pip
(1/26) Installing py3-appdirs (1.4.4-r1)
(2/26) Installing py3-ordered-set (4.0.1-r0)
...

Con esto debería ser suficiente; revisamos el estado actual:

vm01:~# python3 --version
Python 3.8.5
vm01:~# pip --version
pip 20.1.1 from /usr/lib/python3.8/site-packages/pip (python 3.8)

Instalando mkdocs

vm01:~# pip install mkdocs
Collecting mkdocs
  Downloading mkdocs-1.1.2-py3-none-any.whl (6.4 MB)
     |████████████████████████████████| 6.4 MB 5.8 MB/s
Collecting Jinja2>=2.10.1
  Downloading Jinja2-2.11.2-py2.py3-none-any.whl (125 kB)
     |████████████████████████████████| 125 kB 6.2 MB/s
Collecting Markdown>=3.2.1
  Downloading Markdown-3.3.1-py3-none-any.whl (95 kB)
     |████████████████████████████████| 95 kB 3.1 MB/s
Collecting click>=3.3
  Downloading click-7.1.2-py2.py3-none-any.whl (82 kB)
     |████████████████████████████████| 82 kB 747 kB/s
Collecting livereload>=2.5.1
  Downloading livereload-2.6.3.tar.gz (25 kB)
Collecting lunr[languages]==0.5.8
  Downloading lunr-0.5.8-py2.py3-none-any.whl (2.3 MB)
     |████████████████████████████████| 2.3 MB 5.6 MB/s
Collecting tornado>=5.0
  Downloading tornado-6.0.4.tar.gz (496 kB)
     |████████████████████████████████| 496 kB 4.2 MB/s
Collecting PyYAML>=3.10
  Downloading PyYAML-5.3.1.tar.gz (269 kB)
     |████████████████████████████████| 269 kB 4.8 MB/s
Collecting MarkupSafe>=0.23
  Downloading MarkupSafe-1.1.1.tar.gz (19 kB)
Requirement already satisfied: six in /usr/lib/python3.8/site-packages (from livereload>=2.5.1->mkdocs) (1.15.0)
Collecting future>=0.16.0
  Downloading future-0.18.2.tar.gz (829 kB)
     |████████████████████████████████| 829 kB 5.8 MB/s
Collecting nltk>=3.2.5; python_version > "2.7" and extra == "languages"
  Downloading nltk-3.5.zip (1.4 MB)
     |████████████████████████████████| 1.4 MB 4.7 MB/s
Collecting joblib
  Downloading joblib-0.17.0-py3-none-any.whl (301 kB)
     |████████████████████████████████| 301 kB 5.8 MB/s
Collecting regex
  Downloading regex-2020.10.11.tar.gz (690 kB)
     |████████████████████████████████| 690 kB 4.9 MB/s
Collecting tqdm
  Downloading tqdm-4.50.2-py2.py3-none-any.whl (70 kB)
     |████████████████████████████████| 70 kB 4.1 MB/s
Using legacy setup.py install for livereload, since package 'wheel' is not installed.
Using legacy setup.py install for tornado, since package 'wheel' is not installed.
Using legacy setup.py install for PyYAML, since package 'wheel' is not installed.
Using legacy setup.py install for MarkupSafe, since package 'wheel' is not installed.
Using legacy setup.py install for future, since package 'wheel' is not installed.
Using legacy setup.py install for nltk, since package 'wheel' is not installed.
Using legacy setup.py install for regex, since package 'wheel' is not installed.
Installing collected packages: MarkupSafe, Jinja2, Markdown, click, tornado, livereload, future, joblib, regex, tqdm, nltk, lunr, PyYAML, mkdocs
    Running setup.py install for MarkupSafe ... done
    Running setup.py install for tornado ... done
    Running setup.py install for livereload ... done
    Running setup.py install for future ... done
    Running setup.py install for regex ... error
    ERROR: Command errored out with exit status 1:
     command: /usr/bin/python3 -u -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'/tmp/pip-install-nqaj9jv0/regex/setup.py'"'"'; __file__='"'"'/tmp/pip-install-nqaj9jv0/regex/setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(__file__);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' install --record /tmp/pip-record-atsw5nld/install-record.txt --single-version-externally-managed --compile --install-headers /usr/include/python3.8/regex
         cwd: /tmp/pip-install-nqaj9jv0/regex/
    Complete output (17 lines):
    running install
    running build
    running build_py
    creating build
    creating build/lib.linux-x86_64-3.8
    creating build/lib.linux-x86_64-3.8/regex
    copying regex_3/__init__.py -> build/lib.linux-x86_64-3.8/regex
    copying regex_3/regex.py -> build/lib.linux-x86_64-3.8/regex
    copying regex_3/_regex_core.py -> build/lib.linux-x86_64-3.8/regex
    copying regex_3/test_regex.py -> build/lib.linux-x86_64-3.8/regex
    running build_ext
    building 'regex._regex' extension
    creating build/temp.linux-x86_64-3.8
    creating build/temp.linux-x86_64-3.8/regex_3
    gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -fomit-frame-pointer -g -fno-semantic-interposition -fomit-frame-pointer -g -fno-semantic-interposition -fomit-frame-pointer -g -fno-semantic-interposition -DTHREAD_STACK_SIZE=0x100000 -fPIC -I/usr/include/python3.8 -c regex_3/_regex.c -o build/temp.linux-x86_64-3.8/regex_3/_regex.o
    unable to execute 'gcc': No such file or directory
    error: command 'gcc' failed with exit status 1
    ----------------------------------------
ERROR: Command errored out with exit status 1: /usr/bin/python3 -u -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'/tmp/pip-install-nqaj9jv0/regex/setup.py'"'"'; __file__='"'"'/tmp/pip-install-nqaj9jv0/regex/setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(__file__);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' install --record /tmp/pip-record-atsw5nld/install-record.txt --single-version-externally-managed --compile --install-headers /usr/include/python3.8/regex Check the logs for full command output.

Revisando donde se produce el error vemos:

Running setup.py install for regex ... error
    ERROR: Command errored out with exit status 1:
     command: /usr/bin/python3 -u -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'/tmp/pip-install-nqaj9jv0/regex/setup.py'"'"'; __file__='"'"'/tmp/pip-install-nqaj9jv0/regex/setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(__file__);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' install --record /tmp/pip-record-atsw5nld/install-record.txt --single-version-externally-managed --compile --install-headers /usr/include/python3.8/regex
         cwd: /tmp/pip-install-nqaj9jv0/regex/

Instalando todo lo necesario

Como vemos en el mensaje de error, parece que falta el paquete setuptools.

Lo instalamos mediante:

vm01:~# pip install --upgrade setuptools
Collecting setuptools
  Downloading setuptools-50.3.0-py3-none-any.whl (785 kB)
     |████████████████████████████████| 785 kB 2.5 MB/s
Installing collected packages: setuptools
  Attempting uninstall: setuptools
    Found existing installation: setuptools 47.0.0
    Uninstalling setuptools-47.0.0:
      Successfully uninstalled setuptools-47.0.0
Successfully installed setuptools-50.3.0

Reintentamos la instalación de mkdocs, pero vuelve a fallar…

Revisando unas líneas por encima del error en sí mismo, vemos que hay una serie de avisos de que wheel no está instalado:

...
Using legacy setup.py install for PyYAML, since package 'wheel' is not installed.
Using legacy setup.py install for nltk, since package 'wheel' is not installed.
Using legacy setup.py install for regex, since package 'wheel' is not installed.
...

Instalamos el módulo wheel:

# pip install wheel
Collecting wheel
  Downloading wheel-0.35.1-py2.py3-none-any.whl (33 kB)
Installing collected packages: wheel
Successfully installed wheel-0.35.1

De nuevo falla… Nos fijamos ahora en que el problema está en que no encuentra gcc:

...
unable to execute 'gcc': No such file or directory
  error: command 'gcc' failed with exit status 1
  ----------------------------------------
  ERROR: Failed building wheel for regex
...

Instalamos gcc:

apk add gcc

Vuelve a fallar, pero el error es diferente; vemos que no encuentra Python.h:

...
 regex_3/_regex.c:48:10: fatal error: Python.h: No such file or directory
       48 | #include "Python.h"
          |          ^~~~~~~~~~
    compilation terminated.
...

La solución la encontramos en StackOverflow: fatal error: Python.h: No such file or directory; tenemos que instalar python3-dev.

vm01:~# apk add python3-dev
(1/4) Upgrading gdbm (1.13-r1 -> 1.18.1-r0)
(2/4) Upgrading python3 (3.8.5-r0 -> 3.8.6-r0)
(3/4) Installing pkgconf (1.7.3-r0)
(4/4) Installing python3-dev (3.8.6-r0)
Executing busybox-1.31.1-r19.trigger
OK: 1027 MiB in 184 packages

Intentamos instalar de nuevo y obtenemos un error similar al anterior:

...
In file included from regex_3/_regex.c:48:
    /usr/include/python3.8/Python.h:11:10: fatal error: limits.h: No such file or directory
       11 | #include <limits.h>
          |          ^~~~~~~~~~
    compilation terminated.
    error: command 'gcc' failed with exit status 1

De nuevo, la respuesta proviene de StackOverflow: No such file or directory “limits.h” when installing Pillow on Alpine Linux; este error parece específico de Alpine, al usar musl-libc. La solución pasa por instalar musl-dev:

vm01:~# apk add musl-dev
(1/2) Upgrading musl (1.1.24-r9 -> 1.2.1-r2)
(2/2) Installing musl-dev (1.2.1-r2)
OK: 1037 MiB in 185 packages

Esta vez, cuando lanzamos la instalación de mkdocs el proceso de compilación se lanza:

...
Collecting regex
  Using cached regex-2020.10.11.tar.gz (690 kB)
Collecting tqdm
  Using cached tqdm-4.50.2-py2.py3-none-any.whl (70 kB)
Building wheels for collected packages: regex
  Building wheel for regex (setup.py) ... \

… tras unos minutos la compilación finaliza con éxito:

...
  Created wheel for regex: filename=regex-2020.10.11-cp38-cp38-linux_x86_64.whl size=755925 sha256=31dcdcf2b21b001a04a12ac3808a3d36b91bd73884a7e501a3483d7ff84a316c
  Stored in directory: /root/.cache/pip/wheels/c1/71/60/434b56771bf84ab28e88f95ec6e772e3ff42212bb5fb24985f
Successfully built regex
Installing collected packages: regex, tqdm, nltk, lunr, mkdocs
Successfully installed lunr-0.5.8 mkdocs-1.1.2 nltk-3.5 regex-2020.10.11 tqdm-4.50.2
vm01:~#

Validamos la versión instalada:

vm01:~# mkdocs --version
mkdocs, version 1.1.2 from /usr/lib/python3.8/site-packages/mkdocs (Python 3.8)

Instalación de mkdocs-material

La instalación de mkdocs-material (o de sus dependencias) ya no presenta ningún problema y se instala sin novedad:

# pip install mkdocs-material
Collecting mkdocs-material
  Downloading mkdocs_material-6.0.2-py2.py3-none-any.whl (3.9 MB)
     |████████████████████████████████| 3.9 MB 2.4 MB/s
Collecting Pygments>=2.4
...
Installing collected packages: Pygments, mkdocs-material-extensions, pymdown-extensions, mkdocs-material
Successfully installed Pygments-2.7.1 mkdocs-material-6.0.2 mkdocs-material-extensions-1.0.1 pymdown-extensions-8.0.1

TL;DR

Después del proceso de troubleshooting, creo una nueva máquina con Alpine Linux 3.12 para validar la instalación:

Instalación de paquetes de Alpine

Primero, habilitamos el repositorio http://dl-cdn.alpinelinux.org/alpine/edge/community en el fichero /etc/apk/repositories.

apk update
apk add python3 py3-pip gcc python3-dev musl-dev

Instalación de módulos de Python

pip install setuptools wheel mkdocs mkdocs-material

Finalmente, podemos validar que se ha instalado correctamente mediante:

mkdocs --version

Mismo error en MacOS 10.15.7 (Catalina)

Al intentar instalar/actualizar MkDocs en el Mac, obtengo el mismo error relacionado con regex:

$ pip3 install --upgrade mkdocs
...
Building wheels for collected packages: regex
  Building wheel for regex (setup.py) ... error
  ERROR: Command errored out with exit status 1:
   command: /Library/Frameworks/Python.framework/Versions/3.7/bin/python3 -u -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'/private/var/folders/q5/ph3nkkqn2t723nj5m_x0_rvr0000gn/T/pip-install-tovs356k/regex/setup.py'"'"'; __file__='"'"'/private/var/folders/q5/ph3nkkqn2t723nj5m_x0_rvr0000gn/T/pip-install-tovs356k/regex/setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(__file__);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' bdist_wheel -d /private/var/folders/q5/ph3nkkqn2t723nj5m_x0_rvr0000gn/T/pip-wheel-5k1bg8x2
       cwd: /private/var/folders/q5/ph3nkkqn2t723nj5m_x0_rvr0000gn/T/pip-install-tovs356k/regex/

Los módulos de Python ya están instalados:

$ pip install --upgrade setuptools wheel
Requirement already up-to-date: setuptools in /Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages (50.3.0)
Requirement already up-to-date: wheel in /Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages (0.35.1)

La instalación de setuptools para Python 3 en Mac OS la he obtenido de StackOverflow how to install setuptools in mac:

curl https://bootstrap.pypa.io/ez_setup.py -o - | python3

De nuevo vemos que el error viene de intentar compilar wheel, ya que no encuentra gcc.

Las propuestas de instalar XCode usando xcode-select --install fallan con el mensaje de error: `Can’t install the software because it is not currently available from the Software Update server."

Al parecer, el método de instalación vía xcode-select ya no es válido (ver Problem installing Command Line Tools for Mojave) y hay que acceder a More Downloads for Apple Developers` para descargar las herramientas de línea de comando (unos 450MB), que una vez instalados en disco ocupan 2.40GB!!

Tras la instalación de las Command Line tools for XCode 12.1 GM, finalmente se instala mkdocs, aunque con algunos warnings:

...
Installing collected packages: regex, nltk, lunr, mkdocs
  Attempting uninstall: mkdocs
    Found existing installation: mkdocs 1.0.4
    Uninstalling mkdocs-1.0.4:
      Successfully uninstalled mkdocs-1.0.4
ERROR: After October 2020 you may experience errors when installing or updating packages. This is because pip will change the way that it resolves dependency conflicts.

We recommend you use --use-feature=2020-resolver to test your packages with the new resolver before it becomes the default.

mkdocs-material 4.6.0 requires markdown<3.2, but you'll have markdown 3.3 which is incompatible.
mkdocs-material 4.6.0 requires pymdown-extensions<6.3,>=6.2, but you'll have pymdown-extensions 8.0.1 which is incompatible.
mkdocs-material-extensions 1.0.1 requires mkdocs-material>=5.0.0, but you'll have mkdocs-material 4.6.0 which is incompatible.
Successfully installed lunr-0.5.8 mkdocs-1.1.2 nltk-3.5 regex-2020.10.11

Los avisos desaparecen una vez que actualizamos las dependencias:

pip install --upgrade markdown pymdown-extensions mkdocs-material

Finalmente, también en Mac:

> mkdocs --version
mkdocs, version 1.1.2 from /Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/mkdocs (Python 3.7)