# 5. Crear distribuciones compiladas

Nota

This document is being retained solely until the `setuptools` documentation at https://setuptools.readthedocs.io/en/latest/setuptools.html independently covers all of the relevant information currently included here.

Una «distribución compilada» es lo que probablemente conoce como un «paquete binario» o un «instalador» (dependiendo de sus antecedentes). No es necesariamente binario, sin embargo, porque podría contener sólo código fuente de Python o código de bytes; y no lo llamamos paquete, porque esa palabra ya existe en Python. (E «instalador» es un término específico para el mundo de los sistemas de escritorio convencionales.)

Una distribución compilada es la forma de hacer la vida lo más fácil posible para los instaladores de la distribución de módulos: para los usuarios de sistemas Linux basados en RPM, es un RPM binario; para los usuarios de Windows, es un instalador ejecutable; para los usuarios de Linux basados en Debian, es un paquete Debian; y así sucesivamente. Obviamente, ninguna persona podrá crear distribuciones compiladas para cada plataforma existente, por lo que los Distutils están diseñados para permitir que los desarrolladores de módulos se concentren en su especialidad—escribir código y crear distribuciones de fuentes—mientras una especie intermedia llamada empaquetadores surge para convertir las distribuciones de fuentes en distribuciones compiladas para tantas plataformas como empaquetadores.

Por supuesto, el desarrollador del módulo podría ser su propio empaquetador; o el empaquetador podría ser un voluntario «por ahí» en algún lugar que tenga acceso a una plataforma a la que el desarrollador original no tiene; o podría ser software que periódicamente obtiene nuevas distribuciones de fuentes y las convierte en distribuciones compiladas para tantas plataformas como el software tenga acceso. Independientemente de quiénes sean, un empaquetador utiliza el script de configuración y la familia de comandos bdist para generar distribuciones compiladas.

Como un ejemplo sencillo, si ejecuto el siguiente comando en el árbol de código de Distutils:

    python setup.py bdist

a continuación, Distutils crea mi distribución de módulos (el propio Distutils en este caso), realiza una instalación «falsa» (también en el directorio `build`) y crea el tipo predeterminado de distribución compilada para mi plataforma. El formato predeterminado para las distribuciones compiladas es un archivo tar «dumb» en Unix, y un simple instalador ejecutable en Windows. (Ese archivo tar se considera «dumb» porque tiene que ser desempaquetado en una ubicación específica para funcionar.)

Por lo tanto, el comando anterior en un sistema Unix crea `Distutils-1.0.plat.tar.gz`; desempaquetar este tarball desde el lugar correcto instala el Distutils como si hubiera descargado la distribución de fuentes y ejecutado `python setup.py install`. (El «lugar correcto» es la raíz del sistema de ficheros o del directorio `prefix` de Python, dependiendo de las opciones dadas al comando bdist_dumb; por defecto se realizan distribuciones dumb relativas a `prefix`.)

Obviamente, para distribuciones puras de Python, esto es tan simple como ejecutar `python setup.py install`—pero para distribuciones no puras, que incluyen extensiones que tendrían que ser compiladas, puede significar la diferencia entre alguien que puede usar sus extensiones o no. Y crear distribuciones compiladas «inteligentes», como un paquete RPM o un instalador ejecutable para Windows, es mucho más conveniente para los usuarios, incluso si su distribución no incluye ninguna extensión.

El comando bdist tiene una opción `--formats` , similar al comando sdist, que puede utilizar para seleccionar los tipos de distribución compilada a generar: por ejemplo,

    python setup.py bdist --format=zip

cuando se ejecuta en un sistema Unix, crearía `Distutils-1.0.plat.zip`—de nuevo, este archivo sería desempaquetado desde el directorio raíz para instalar Distutils.

Los formatos disponibles para distribuciones compiladas son:

Formato | Descripción | Notas
--- | --- | ---
`gztar` | archivo gzipped tar (`.tar.gz`) | (1)
`bztar` | archivo bzipped tar (`.tar.bz2`) |
`xztar` | archivo xzipped tar (`.tar.xz`) |
`ztar` | archivo tar comprimido (`.tar.Z`) | (3)
`tar` | archivo tar (`.tar`) |
`zip` | archivo zip (`.zip`) | (2),(4)
`rpm` | RPM | (5)
`pkgtool` | Solaris pkgtool |
`sdux` | HP-UX swinstall |
`wininst` | archivo ZIP autoextraíble para Windows | (4)
`msi` | Instalador Microsoft. |

Distinto en la versión 3.5: Añadido soporte para el formato `xztar`.

Notas:

1. predeterminado en Unix

2. predeterminado en Windows

3. requiere utilidad externa compress .

4. requiere o bien la utilidad externa zip o bien el módulo `zipfile` (parte de la librería estándar de Python desde Python 1.6)

5. requiere la utilidad externa rpm, versión 3.0.4 o mejor (usar `rpm --version` para descubrir que versión tiene)

No tiene que usar el comando bdist con la opción `--formats`; también puede usar el comando que directamente implementa el formato en el que esté interesado. Algunos de estos subcomandos bdist de hecho generan varios formatos similares; por ejemplo, el comando bdist_dumb genera todos los formatos de archivo «dumb» (`tar`, `gztar`, `bztar`, `xztar`, `ztar`, y `zip`), y bdist_rpm genera tanto binario como fuentes RPMs. Los subcomandos bdist, y los formatos generados por cada uno, son:

Comando | Formatos
--- | ---
bdist_dumb | tar, gztar, bztar, xztar, ztar, zip
bdist_rpm | rpm, srpm
bdist_wininst | wininst
bdist_msi | msi

Nota

bdist_wininst se encuentra obsoleto desde Python 3.8.

Nota

bdist_msi está deprecado desde Python 3.9.

Las siguientes secciones proporcionan detalles sobre los comandos individuales de bdist_* .

## 5.1. Creando paquetes RPM

El formato RPM se usa en muchas distribuciones de Linux populares, incluyendo Red Hat, SuSE, y Mandrake. Si alguna de éstas (o cualquier otra distribución de Linux basada en RPM) es su entorno habitual, crear paquetes RPM para otros usuarios de la misma distribución es trivial. Dependiendo de la complejidad de su módulo de distribución y las diferencias entre distribuciones Linux, también podrá crear RPMs que funcionen en distribuciones diferentes basadas en RPM.

El forma más común de crear un RPM de su distribución de módulo es ejecutar el comando bdist_rpm

    python setup.py bdist_rpm

o el comando bdist con la opción `--format`

    python setup.py bdist --formats=rpm

El primero permite especificar las opciones específicas de RPM; el segundo permite especificar fácilmente varios formatos en una ejecución. Si necesita hacer los dos, se pueden especificar explícitamente múltiples comandos bdist_* y sus opciones:

    python setup.py bdist_rpm --packager="John Doe <jdoe@example.org>" \
                    bdist_wininst --target-version="2.0"

La creación de paquetes RPM es impulsada por un archivo `.spec`, al igual que el uso de Distutils es impulsado por el script de configuración. Para hacer su vida más fácil, el comando bdist_rpm normalmente crea un archivo `.spec` basado en la información que usted proporciona en el script de configuración, en la línea de comandos y en cualquier archivo de configuración de Distutils. Varias opciones y secciones en el archivo `.spec` se derivan de las opciones del script de configuración de la siguiente manera:

Opción de archivo RPM `.spec` o sección | Opción del script de instalación de Distutils
--- | ---
Nombre | `name`
Resumen (en el preámbulo) | `description`
Versión | `version`
Vendedor | `author` y `author_email`, o — & `maintainer` y `maintainer_email`
Copyright | `license`
Url | `url`
%description (sección) | `long_description`

Adicionalmente, hay muchas opciones en los archivos `.spec` que no se corresponden a opciones en el script de configuración. La mayoría de éstas están manejadas a través de opciones al comando bdist_rpm como sigue:

Opción de archivo RPM `.spec` o sección | Opción bdist_rpm | valor predefinido
--- | --- | ---
Release | `release` | «1»
Grupo | `group` | «Desarrollo/Librerías»
Vendedor | `vendor` | (ver arriba)
Empaquetador | `packager` | (ninguno)
Proporciona | `provides` | (ninguno)
Requiere | `requires` | (ninguno)
Conflictos | `conflicts` | (ninguno)
Obsolescencias | `obsoletes` | (ninguno)
Distribución | `distribution_name` | (ninguno)
Requisitos de compilación | `build_requires` | (ninguno)
Icono | `icon` | (ninguno)

Obviamente, proporcionar incluso algunas de estas opciones en la línea de comandos sería tedioso y propenso a errores, por lo que generalmente es mejor ponerlas en el archivo de configuración de instalación,:file:setup.cfg— ver sección Escribiendo el archivo de configuración de instalación (setup). Si distribuye o empaqueta muchas distribuciones de módulos de Python, es posible que desee incluir opciones que se apliquen a todas ellas en su archivo de configuración personal de Distutils (`~/.pydistutils.cfg`). Si desea deshabilitar temporalmente este archivo, puede pasar la opción `--no-user-cfg` a `setup.py`.

Hay tres pasos para construir un paquete RPM binario, los cuales son manejados automáticamente por Distutils:

1. crear un archivo `.spec`. que describe el paquete (análogo al script de configuración de Distutils; de hecho, gran parte de la información en el script de configuración termina en el fichero `.spec` )

2. crear el fuente RPM

3. crear el «binario» RPM (que puede o no contener código binario, dependiendo de si la distribución de su módulo contiene extensiones Python)

Normalmente, RPM agrupa los dos últimos pasos; cuando usa las Distutils, los tres pasos generalmente están agrupados.

Si lo desea, puede separar estos tres pasos. Puede usar la opción `--spec-only` para hacer bdist_rpm simplemente cree el archivo `.spec` y salga; en este caso, el archivo `.spec` se escribirá en el «directorio de distribución»— normalmente `dist/`, pero se puede personalizar con la opción `--dist-dir`. (Normalmente, el archivo `.spec` termina en lo más profundo del «árbol de compilación», en un directorio temporal creado por bdist_rpm.)

## 5.2. Crear instaladores de Windows

Advertencia

bdist_wininst se encuentra obsoleto desde Python 3.8.

Advertencia

bdist_msi está deprecado desde Python 3.9.

Los instaladores ejecutables son el formato natural para distribuciones binarias en Windows. Muestran una agradable interfaz gráfica de usuario, con información sobre la distribución del módulo que se instalará extraída de los metadatos en el script de configuración, permiten al usuario seleccionar algunas opciones e iniciar o cancelar la instalación.

Dado que los metadatos se toman del script de configuración, crear instaladores de Windows suele ser tan fácil como ejecutar:

    python setup.py bdist_wininst

o el comando bdist con la opción `--formats`

    python setup.py bdist --formats=wininst

Si tiene una distribución de módulo pura (sólo conteniendo módulos de Python puros y paquetes), el instalador resultante será independiente de la versión y tendrá un nombre similar a `foo-1.0.win32.exe`. Tenga en cuenta que crear distribuciones binarias `wininst` sólo se soporta en sistemas Windows.

Si tiene distribuciones no puras, las extensiones sólo se pueden crear en una plataforma Windows, y serán dependientes de la versión de Python. El nombre de archivo del instalador reflejará esto y ahora tiene la forma `foo-1.0.win32-py2.0.exe`. Debe crear un instalador separado para cada versión de Python que se quiera soportar.

El instalador intentará compilar módulos puros en bytecode después de la instalación en el sistema de destino en modo normal y optimizado. Si no desea que esto suceda por alguna razón, puede ejecutar el comando bdist_wininst con la opción `--no-target-compile` o la opción `--no-target-optimize`.

Por defecto, el instalador mostrará el genial logo «Python Powered» cuando se ejecute, pero puede también proporcionar su propio mapa de bits de 152x261 que debe ser un archivo Windows `.bmp` con la opción `--bitmap`.

El instalador también mostrará un título grande en la pantalla de fondo del escritorio cuando se ejecute, que se construye a partir del nombre de su distribución y el número de versión. Esto se puede cambiar a otro texto usando la opción `--title`.

El archivo de instalación se escribirá en el «directorio de distribución» — normalmente `dist/`, pero se puede personalizar con la opción `--dist-dir`.

## 5.3. Compilación cruzada en Windows

A partir de Python 2.6, distutils es capaz de realizar una compilación cruzada entre plataformas Windows. En la práctica, esto significa que con las herramientas correctas instaladas, puede usar una versión de Windows de 32bit para crear extensiones de 64bit y viceversa.

Para compilar para una plataforma alternativa, especifique la opción `--plat-name` del comando de compilación. Los valores válidos son actualmente “win32”, y “win-amd64”. Por ejemplo, en una versión de Windows de 32bit, puede ejecutar:

    python setup.py build --plat-name=win-amd64

para crear una versión de 64bit de su extensión. Los instaladores de Windows también admiten esta opción, por lo que el comando:

    python setup.py build --plat-name=win-amd64 bdist_wininst

crearía un ejecutable de instalación de 64bit en su versión de 32bit de Windows.

Para realizar una compilación cruzada, debe descargar el código fuente de Python y compilar el propio Python para la plataforma a la que se dirige; no es posible desde una instalación binaria de Python (ya que el archivo .lib etc para otras plataformas no está incluido). En la práctica, esto significa que el usuario de un sistema operativo de 32bit necesitará usar Visual Studio 2008 para abrir la solución `PCbuild/PCbuild.sln` en el árbol de código de Python y construir la configuración » x64 » del proyecto “pythoncore” antes de que sea posible la compilación cruzada de extensiones.

Tenga en cuenta que, de forma predeterminada, Visual Studio 2008 no instala compiladores o herramientas de 64bit. Es posible que deba volver a ejecutar el proceso de instalación de Visual Studio y seleccionar estas herramientas (usar Panel de control -> [Agregar/Quitar] Programas es una forma conveniente de verificar o modificar su instalación existente).

### 5.3.1. El script posterior a la instalación

A partir de Python 2.3, se puede especificar un script posterior a la instalación con la opción `--install-script` . Se debe especificar el nombre base del script y el nombre del archivo del script también se debe incluir en el argumento de los scripts de la función de configuración.

Este script se ejecutará en el momento de la instalación en el sistema de destino después de que se hayan copiado todos los archivos, con `argv[1]` establecido en `-install`, y nuevamente en el momento de la desinstalación antes de que se eliminen los archivos con `argv[1]` establecido en `-remove`.

El script de instalación se ejecuta incrustado en el instalador de Windows, cada salida (`sys.stdout`, `sys.stderr`) se redirige a un búfer y se mostrará en la GUI una vez finalizado el script.

Algunas funciones especialmente útiles en este contexto están disponibles como funciones integradas adicionales en el script de instalación.

`directory_created`(path)
`file_created`(path)

  Estas funciones deben llamarse cuando el script posterior a la instalación crea un directorio o archivo en el momento de la instalación. Registrará path con el des-instalador, de modo que se eliminará cuando se desinstale la distribución. Para mayor seguridad, los directorios solo se eliminan si están vacíos.

`get_special_folder_path`(csidl_string)

  Esta función se puede utilizar para recuperar ubicaciones de directorios especiales en Windows como el menú Inicio o el escritorio. Retorna la ruta completa al directorio. csidl_string debe ser una de las siguientes cadenas:

      "CSIDL_APPDATA"

      "CSIDL_COMMON_STARTMENU"
      "CSIDL_STARTMENU"

      "CSIDL_COMMON_DESKTOPDIRECTORY"
      "CSIDL_DESKTOPDIRECTORY"

      "CSIDL_COMMON_STARTUP"
      "CSIDL_STARTUP"

      "CSIDL_COMMON_PROGRAMS"
      "CSIDL_PROGRAMS"

      "CSIDL_FONTS"

  Si no se puede recuperar el directorio, se lanza `OSError`.

  Los directorios disponibles dependen de la versión exacta de Windows y probablemente también de la configuración. Para obtener más información, consulte la documentación de Microsoft de la función `SHGetSpecialFolderPath()` .

`create_shortcut`(target, description, filename[, arguments[, workdir[, iconpath[, iconindex]]]])

  Esta función crea un atajo. target es la ruta al programa que se iniciará con el acceso directo. description es la descripción del atajo. filename es el título del acceso directo que verá el usuario. arguments especifica los argumentos de la línea de comandos, si los hay. workdir es el directorio de trabajo del programa. iconpath es el archivo que contiene el icono del acceso directo, y iconindex es el índice del icono en el archivo iconpath. Nuevamente, para obtener más detalles, consulte la documentación de Microsoft para la interfaz `IShellLink`.

## 5.4. Vista User Access Control (UAC)

A partir de Python 2.6, bdist_wininst admite una opción `--user-access-control`. El valor predeterminado es “none” (lo que significa que no se realiza ningún gestión de UAC), y otros valores válidos son “auto” (lo que significa que se solicita la elevación de UAC si Python se instaló para todos los usuarios) y “force” (que significa que siempre se solicita la elevación).

Nota

bdist_wininst se encuentra obsoleto desde Python 3.8.

Nota

bdist_msi está deprecado desde Python 3.9.