- Rust 78.3%
- Shell 21.1%
- Makefile 0.6%
|
|
||
|---|---|---|
| misc/cmp | ||
| sh | ||
| src | ||
| .gitignore | ||
| .rustfmt.toml | ||
| AGENTS.md | ||
| Cargo.lock | ||
| Cargo.toml | ||
| CLAUDE.md | ||
| LICENSE-0BSD | ||
| LICENSE-GLWTPL | ||
| LICENSE-WTFPL | ||
| Makefile | ||
| README.md | ||
ljh
package management infrastructure
/ˌl̪'/
Information
ljh is not a package manager. Its primary goal is to make building and maintaining packages from recipes trivial.
ljh is capable of dependency resolution and fetching sources, among a few other things. It can install packages, but can't remove them, since that's without the scope of building packages. Basic installation logic is necessary as builds occur in a container, and dependencies exist.
ljh is technically passable as a package manager if you intend to update packages by reinstalling your entire system from a specified list of packages. However, a front-end is being developed.
Builds are hermetic and unprivileged, thanks to bubblewrap.
Recipes
A reference recipe repository implementation exists at https://git.gay/tox/recipes.
The master branch defines packages used on my primary system.
A basic recipe might look something like this:
name=egl-wayland
version=1.1.21
release=0
container=ljh@20260506
about="Wayland EGL external platform library"
licenses=("MIT")
upstream="https://github.com/NVIDIA/$name"
packagers=("m: tox <tox@tox.wtf>")
sources=(
"$upstream/archive/$version.tar.gz % YZV4NA9H_glOsvFqPzZmWg"
)
dependencies=(
libdrm
wayland
libglvnd
eglexternalplatform
b:wayland-protocols
)
pkg() {
w meson
defmeson
}
# vi: set ts=4 sw=4 tw=0 ft=bash :
At the top exist some variables defining the base package. The required ones are as follows:
$name: The base package's name.$version: The package's version. This may not contain a dash, and should be limited to ASCII.$release: A 0-indexed u64 denoting the nth build of this version. This is generally incremented as patches are made, or when rebuilding against updated dependencies.$container: The container in which this package is built.
Though unused by ljh, other arbitrary variables are collected by ljh and serialized to JSON, for use by a front-end. I like to include the following:
$about: A brief description of the package.$licenses: SPDX-compliant license identifiers for the package.$upstream: The package's upstream.$packagers: The people who built the package. Maintainers are denoted withm:and contributors withc:.
The $sources and $dependencies arrays are both technically optional as
well, but many packages will want them.
A source consists of up to three parts -- the URL, the destination file, and a b23 checksum. A complete example might look like so:
"$upstream/archive/$version.tar.gz -> $name-$version.tar.gz % YZV4NA9H_glOsvFqPzZmWg"
A dependency consists of up to two parts -- the dependency kind, and a package name. If no dependency kind is specified, the dependency is required. Dependency kinds are as follows:
b:: Build-time dependencies. For instance,b:rustcorb:wayland-protocols.i:: Install-time dependencies. For instance,i:xdg-utilsori:desktop-file-utils.
The $opts array (not showcased here) is used to pass options to ljh. For
instance, opts=(net !lto) will enable networking in the container and remove
LTO flags from certain environment variables.
Options are defined under /sh/opts/.
Containers
Containers can be built by hand, but I generate mine with LFStage to save some time (and headache).
A container has roughly the following file hierarchy:
ljh@20260506/
├── lower/
│ ├── base/
│ │ ├── bin -> usr/bin/
│ │ ├── dev/
│ │ ├── etc/
│ │ ├── home/
│ │ ├── lib -> usr/lib/
│ │ ├── lib64/
│ │ ├── ljh/
│ │ ├── proc/
│ │ ├── run/
│ │ ├── sbin -> usr/sbin/
│ │ ├── sys/
│ │ ├── tmp/
│ │ ├── usr/
│ │ └── var/
│ └── net/
│ └── etc/
├── upper/
│ ├── D/
│ │ ├── libtree/
│ │ ├── libtree@95d8992968a18ae957fef723f103b416d0843398-0.tar.zst
│ │ ├── libtree.man/
│ │ └── libtree.man@95d8992968a18ae957fef723f103b416d0843398-0.tar.zst
│ ├── ljh/
│ │ └── .cache/
│ └── S/
│ └── libtree-95d8992968a18ae957fef723f103b416d0843398/
└── work/
└── work/
lower/base/contains a read-only version of the container's root filesystem. It's quite similar to a Gentoo stage file.lower/net/contains read-only networking information sourced from the host. This allows the container to use the same DNS settings and CA certificates as the host (which is usually desired).upper/holds all written changes. This is where dependencies are installed and packages are built.upper/D/contains the DESTDIRs for package installations.upper/S/contains the sources for package builds, and builds take place in the directory extracted from the first specified source archive. In the event no such directory is available, builds begin fromupper/S.work/work/is used by the kernel and may be ignored for our purposes.
The generic ljh container is available at https://git.gay/tox/ljh-lfstage.
In the future, containers will be responsible for providing ljh.
Getting Started
Note
Before we begin, it's worth noting that something here is likely to break. But the general idea should remain the same: build a container, install ljh, add recipes, make any needed tweaks, and build packages.
We'll begin from scratch.
First, acquire a copy of LFStage:
git clone https://github.com/tox-wtf/lfstage && cd lfstage
make
sudo make install
Note
The
lfstagecommand will likely take a long time to complete. Feel free to do something else in the meantime.
Then, you'll need a build container. Let's build the standard one:
sudo git clone https://git.gay/tox/ljh-lfstage /var/lib/lfstage/profiles/ljh
sudo lfstage build ljh
And copy it into place:
sudo cp /var/cache/lfstage/stages/lfstage-ljh-*.tar.xz ~/.ljh/containers/ljh@$(date +%Y%m%d).tar.xz
Once that's done, install ljh:
git clone https://git.gay/tox/ljh && cd ljh
make
make install
make CONTAINER=ljh@$(date +%Y%m%d) install-container
Copy some recipes into place:
git clone https://git.gay/tox/recipes ~/.ljh/recipes
And build a package:
ljh build libtree
This will probably fail since the date of the container specified in the recipe likely differs from the one you just built. To address this, update the recipe:
sed "/^container=/s/=.\+/=ljh@$(date +%Y%m%d)/" -i ~/.ljh/recipes/libtree/recipe
Note
You should increment package releases when updating their container, but since you haven't built anything yet, it doesn't really matter.
Then try building it again:
ljh build libtree
You can update the container for all recipes with the following command:
sed "/^container=/s/=.\+/=ljh@$(date +%Y%m%d)/" -i ~/.ljh/recipes/*/recipe
Once you're content with your recipes, you can build everything with the following command:
ljh build
Tip
You shouldn't actually use my recipes. You can reference them, but this whole section ultimately just exists to document getting started.
Good luck!
Licensing
ljh is not ready for use by people who aren't me yet, but plunder whatever you like. ljh is licensed under your choice of any of the following: