AnotherOS

Documentation

Ports and packaging

The toolchain, the on-device compiler, how to write a port, and how packages are installed.

The toolchain

The cross toolchain is a real triplet — x86_64-anotheros-musl — built in stages: binutils, a stage-one GCC, musl, then a stage-two GCC with libstdc++.

It replaced an earlier arrangement of a generic bare-metal toolchain plus a define. The difference matters: the platform predefine comes from a real triplet, and the system headers live in the sysroot where a configure script expects to find them.

C++ has POSIX threads, so std::mutex works. That is what made WebKit possible.

The machine builds its own software

AnotherOS carries its own compiler. On a freshly installed disk:

cc hello.c -o hello && ./hello

works with no cross host.

This is a real native GCC, not a wrapper: cc, gcc, g++, cpp and the binutils suite over a complete sysroot with headers, startup files and shared libc, libgcc and libstdc++.

It lives under a fixed prefix, is linked into the standard binary directory, and is first on the path. The installer keeps it by default, so every install self-hosts; there is an option for a runtime-only disk that leaves it out.

It is proven by installing to a disk, booting the installed root — a read-only live boot cannot demonstrate a property of a persistent disk — and compiling, linking and running C and C++ on the machine itself.

The rule every port must follow

A port must identify the operating system as AnotherOS.

Configure scripts get the real platform token. Faking a different platform to get past a check is not acceptable. A build that only works because it lied about the platform will be wrong later, somewhere much harder to find.

Linking

Everything is dynamic by default. Build shared objects, not static archives, and prefer shared wherever a port offers the choice.

Vendored libraries: only bytes cross the boundary

A port that embeds its own copy of a library the system also ships must never pass that library's objects across the boundary, in either direction. Only raw bytes — pixels, buffers, strings.

The worked example is worth reading twice, because the failure mode is so quiet. A browser embedded its own copy of a 2D graphics library at one version while the toolkit it linked used the system copy at an older version. One call handed a toolkit-owned surface object into the embedded library's code. The newer code read the older structure at the wrong offsets, declared a perfectly healthy surface broken, and the browser window stayed white. No crash, no failing system call, nothing in any log naming the cause.

It does not matter that the two libraries expose the same API. An object's layout belongs to the implementation that allocated it. Upstream projects survive the same crossing only while their system library happens to match their vendored one — version skew is when it detonates.

Writing a port

Each port is a self-contained fetch, patch and cross-compile recipe that produces an install tree.

That tree is not free-form: the packaging recipe copies it into the filesystem layout below, so a port that installs into the wrong place produces a package that does not work rather than one that works differently.

The filesystem layout

Applications live in a versioned layout — each package under its own name and version — with a system-wide index of links pointing at the current version of each. That is what makes an install atomic and a rollback a matter of flipping links rather than restoring files.

Packages

apkg installs, upgrades, removes and queries packages from a repository.

apkg install <Name>
apkg upgrade
apkg info <Name>
apkg install --force <Name>

Same version, different package

Packages are keyed by name and version, and a rebuild does not have to change the version — so two different builds can legitimately share one version number.

That caused a real problem: a machine holding a build with a known bug was told it was up to date, because the version matched.

So upgrade now compares the checksum recorded at install time against the published one, and acts on a changed digest at an equal version. apkg info prints the installed and published digests side by side, and an install predating that record reports "cannot tell" rather than guessing in either direction.

--force re-downloads and re-installs at the same version. It bypasses the download cache and still enforces the checksum: "reinstall this" is not "install whatever arrives". It does not recurse into dependencies.

Failure behaviour

An install whose source is unreachable fails and installs nothing. There is no failover to a stale cached copy, and a successful HTTP response carrying zero bytes is treated as unreachable rather than as an empty package.

Kernel packages

The kernel itself can be installed as a package, and a new one can be tried once without becoming the default — so a kernel that fails to boot leaves the machine able to return to the previous one by power-cycling it. See UEFI boot entries.

Application bundles

An application ships as a bundle with its own metadata, icon and declared document types. See the file manager for what those declarations do.