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.
More documentation
- Booting the live image
- Installing to disk
- Packages and the filesystem
- How it boots
- The syscall ABI
- Hardware support
- ACPI
- UEFI boot entries
- NetBooting a Mac
- The AO toolkit
- Grafika — the graphics API
- The display protocol
- Desktop effects
- The level bezel
- Notifications
- Rastinis — the file manager
- File operations
- Audio — Garsas
- Hardware video decode
- Networking — Tinklas
- Network shares — SMB and NFS
- Remote control
- The Win32 subsystem
- DirectX and OpenGL for Win32 programs