# pyenv-binary (experimental)

Package an installed Python version into a relocatable archive that can be
installed on another machine.

This is experimental and intentionally decoupled: it does not change
`pyenv install` or any other command. You drive it explicitly through
`pyenv binary`. Run `pyenv binary <command> --help` for details on a command.

## Portability

An archive is portable across machines that share its build platform (OS,
architecture and a compatible libc) and have the recorded system libraries. It
is not portable across, say, glibc and musl, or to an older glibc; the platform
and dependency metadata exist to catch that.

## Commands

### `pyenv binary package <version>[:<entry>] --archive-base-url <url>`

Installs `<version>` from source under a separate name, packages that install
with `save`, then emits a python-build definition for it with
`generate-installer`. With no explicit entry, the name is generated from the
current platform, platform version and architecture. An explicit entry keeps
the existing custom-build workflow.

```sh
pyenv binary package 3.12.7 \
  --archive-base-url https://example.com/binaries
# On Debian 12 x86_64, writes 3.12.7-debian-12-x86_64.tar.gz,
# its .meta file and a `3.12.7-debian-12-x86_64' definition.

pyenv binary package 3.12.7:company-python \
  --archive-base-url https://example.com/binaries
```

The archive, metadata and definition land in the current directory, named after
the entry. Host the archive under `<url>` and drop the definition into
python-build's definition directory.

### `pyenv binary package-name <version>`

Prints the automatically generated entry name without building anything. Linux
uses the distribution name and version, macOS uses the macOS version, and other
systems use the name and release reported by `uname`. All names include the
architecture.

```sh
pyenv binary package-name 3.12.7
# 3.12.7-debian-12-x86_64
```

### `pyenv binary save <version> [<output-dir>] [--name <name>]`

Packs an installed version into `<version>-<platform>.tar.gz` (relative paths)
and writes `<version>-<platform>.meta` describing the build platform (OS, arch,
distro and libc version) and the system libraries the build links against. Use
`--name` to set a different base name for both files.

```sh
pyenv binary save 3.12.7 ./dist
```

### `pyenv binary generate-installer <metadata-file> --archive-url <url> [-o <output>]`

Reads a `.meta` file and emits a python-build definition. Drop it into
python-build's definition directory and `pyenv install <name>` installs the
archive like any other version. The archive location is a parameter, so you can
host it anywhere (it does not have to be a pyenv location); the archive itself
must sit next to the `.meta` file so its checksum can be baked into the
definition.

The definition refuses to install on a different OS/architecture, or an older
glibc, than the archive was built for, and checks that the system libraries it
needs are present.

```sh
pyenv binary generate-installer ./dist/3.12.7-linux-x86_64.meta \
  --archive-url https://example.com/3.12.7-linux-x86_64.tar.gz \
  -o "$(pyenv root)/plugins/python-build/share/python-build/3.12.7-linux-x86_64"

pyenv install 3.12.7-linux-x86_64
```

### `pyenv binary relocate <prefix>`

Rewrites the rpaths of a Python tree unpacked into `<prefix>` so the interpreter
and its extension modules load the bundled libraries from there rather than from
the path the archive was built at. Uses `patchelf`. The generated definition
calls this; you rarely run it by hand.

Relocation is implemented for Linux; macOS is not wired up yet.
