(ab)using the python packaging ecosystem for non-python tools
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-08-15 20:23:43 -04:00
examples fixup pyprojects 2026-08-15 18:05:26 -04:00
.gitignore add examples, clean up structure 2026-08-15 14:39:07 -04:00
.python-version add bat example 2026-08-15 16:34:48 -04:00
pipshim.py v0.1.2: fix double fetch and version num 2026-08-15 20:23:43 -04:00
pyproject.toml v0.1.2: fix double fetch and version num 2026-08-15 20:23:43 -04:00
README.md v0.1.2: fix double fetch and version num 2026-08-15 20:23:43 -04:00
uv.lock remove symlinks, add compositional example 2026-08-15 17:22:31 -04:00

pipshim

A PEP-517 compatible build backend for making non-Python tools installable via pip-compatible package managers.

Why

Publishing consumable software for multiple platforms (especially Linux and Windows) is a pain in the ass. Rather than invent a new package manager for everyone to adopt, this tool tries to solve that problem by adapting to a tool that everyone has, pip!

pip gives us the following things:

  • Broad distribution and usage
  • Centralized updates and version pinning (depending on package manager)
  • Auditability
  • Build isolation
  • Virtual environment support
  • Compatibility with existing package repositories and tooling
  • A lot more

Outputs

pipshim builds the following files:

  • A source distribution, which contains the source code for the project and the code required to build it. Consumers can install this file and build the tool locally on their own systems. It is not architecture or platform-specific.
  • A wheel file, which is a compiled ZIP distribution which is essentially extracted and copied into the Python site-packages folder. This output is host-and-architecture specific as it (can) contains compiled code. pipshim assumes all tools built are host-and-architecture specific.

Most consumers will want to install the wheel file, as it is much faster to install and doesn't require build tools to be installed on their system.

Expectations

  • pipshim doesn't support cross-compilation. The source distribution is platform-agnostic, but the wheel file must be built on the host platform.
  • Binary-only software isn't really supported, for the same reason.
  • Presence of the tooling for compiling the source distribution into a wheel file is left up to the package author. It's possible to package required toolchains themselves with pipshim and provide them as build dependencies.
  • pipshim won't de-reference symlinks outside of your repo. Ensure your code is self-contained. (except for pyproject.toml and pipshim.py)
  • pipshim won't manage your build tools' sandboxes. A golang-based source_distribution will use the build host's GOPATH to cache dependencies.
  • Editable builds don't really work. pipshim can only build your code when your package manager reinstalls your package.

Usage

(TODO, better packaging)

  1. Create a pyproject.toml:
[project]
name = "my-awesome-tool"
version = "0.1.0" 
description = "my-awesome-tool"
readme = "README.md"
authors = [
    #...
]
requires-python = ">=3.8"  # Generated wrapper scripts require v3.8 or newer
dependencies = [] # Leave blank

[build-system]
requires = []  # Todo: better packaging
build-backend = "pipshim"
backend-path = ["."]

[build-system]
requires = ["pipshim @ git+https://gitea.wesj.app/wjordan/[email protected]"]
build-backend = "pipshim"

[tool.pipshim]
## Source distribution options:
# If packaging an external project, provide the git URL and target git ref to build from.
source_git_url = "https://github.com/example/example.git"
source_git_ref = "0.1.0"  # defaults to project.version if not set
# patch_step = ["git", "lfs", "pull"]
# If packging a local project, use sources to specify which files and folders are included in the source.
# sources = ["src", "Makefile"]

## Wheel build options
# configure_step = ["./configure", "--with-something"]
build_step = ["make"]

# output executables generated by the build step. 
# These will be added to PATH by creating a self-destructing python wrapper script.
[tool.pipshim.executables]
example = "out/example"  # format: <COMMAND> = <PATH TO BUILT EXECUTABLE> 

# other non-executable files required for distribution.
[tool.pipshim.dists]
include = "include/"  # format: <INSTALLED PATH> = <PATH IN SOURCE>
"lib/mylib.a" = "out/mylib.a"
  1. Build with your preferred Python packaging tool, e.g. uv build.
  2. Consumers can install the wheel file that matches their platform with any pip-compatible tool, e.g. pip install my-awesome-tool-0.1.0-py3.8-none-macosx_11_0_arm64.whl
  3. The executables in tool.pipshim.executables will be symlinked into the virtual environment's PATH. Python's packaging only supports adding Python scripts into the PATH, so pipshim generates a Python wrapper script for each executable that replaces itself with a symlink to the actual executable file when it runs.
 python3 -m venv .venv
 . .venv/bin/activate
 pip install odin-2026.08.dev.1-py3.8-none-macosx_11_0_arm64.whl
Processing odin-2026.08.dev.1-py3.8-none-macosx_11_0_arm64.whl
Installing collected packages: odin
Successfully installed odin-2026.8.dev1

[notice] A new release of pip is available: 26.1.2 -> 26.2.1
[notice] To update, run: pip install --upgrade pip
 odin version
pipshim v0.1.0: installed simlink  /Users/user/tmp/.venv/bin/odin  =>  /Users/user/tmp/.venv/lib/python3.14/site-packages/odin
/Users/user/tmp/.venv/bin/odin version dev-2026-08
# Wrapper disappears after first call
 odin version
odin version dev-2026-08
# Binary is stored in virtualenv's site-packages folder
 readlink $(which odin)
/Users/user/tmp/.venv/lib/python3.14/site-packages/odin