5. Crear distribuciones compiladas
**********************************

Nota:

  Este documento se conserva únicamente hasta que la documentación de
  "setuptools" en
  https://setuptools.readthedocs.io/en/latest/setuptools.html cubra de
  forma independiente toda la información relevante que se incluye
  actualmente aquí.

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*          | (1)       |
|               | (".tar.gz")                    |           |
+---------------+--------------------------------+-----------+
| "bztar"       | archivo *bzipped tar*          |           |
|               | (".tar.bz2")                   |           |
+---------------+--------------------------------+-----------+
| "xztar"       | archivo *xzipped tar*          |           |
|               | (".tar.xz")                    |           |
+---------------+--------------------------------+-----------+
| "ztar"        | archivo *tar* comprimido       | (3)       |
|               | (".tar.Z")                     |           |
+---------------+--------------------------------+-----------+
| "tar"         | archivo *tar* (".tar")         |           |
+---------------+--------------------------------+-----------+
| "zip"         | archivo *zip* (".zip")         | (2),(4)   |
+---------------+--------------------------------+-----------+
| "rpm"         | *RPM*                          | (5)       |
+---------------+--------------------------------+-----------+
| "pkgtool"     | Solaris **pkgtool**            |           |
+---------------+--------------------------------+-----------+
| "sdux"        | HP-UX **swinstall**            |           |
+---------------+--------------------------------+-----------+
| "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_msi**              | *msi*                                 |
+----------------------------+---------------------------------------+

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>"

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" | Opción **bdist_rpm**          | valor predefinido         |
| o sección                       |                               |                           |
|=================================|===============================|===========================|
| *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. 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 64 bits de su extensión.

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.2.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".
