| mp | index | guide | reference |
two halves, cleanly split, communicating only through argv and the filesystem. no shared library, no ipc beyond "run this backend command with these arguments".
src/*.pl and src/mplib/*.pm, perl, is everything that decides things: config layering, dependency resolution, slot/use composition, hooks, patches. mp itself (src/mp.pl) is a thin dispatcher: it finds a same-directory mp.<command> file matching argv and execs it. every mp.<command> is its own standalone script, parse_flags then config::load then init() then whatever that command does, with no shared main loop. each command is independently runnable and independently readable.
src/*.c, building mpx, c89, is everything that touches the filesystem in a way that has to be fast, portable, and boring: copying a port directory into its staging workspace, snapshotting an install prefix before and after a phase runs (to learn what files a phase actually touched, without the port having to enumerate them itself), parsing mpx.conf, the optional linux sandbox. built with an unusually strict warning and sanitizer set (see mk.conf's ccflgs and sanflgs), precisely because it's the one place a subtle bug corrupts real files on a real filesystem instead of just producing a wrong perl data structure.
mplib's modules, briefly:
config.pm - read_conf and write_conf (the shared "<name>:"-sectioned key=value grammar every *.conf file in this tree uses), load_overlay (OVERLAYS= and PROFILE= chaining), and load() itself, which layers system config, user config, --config= overlays, profile= (lowest precedence of all despite reading last, it only fills in what nothing else set), a --sysroot= overlay, and the mp-managed pkgconf.conf overlay (highest of all), producing the single $CFG every other module reads.
resolve.pm - the largest module. find_portdir and all_ports (repo-priority-ordered port lookup), effective_deps (a port's dependency list after deps/deps+/deps- overrides and use-flag-contributed deps), install_pkg, remove_pkg, reinstall_pkg (the actual install and remove pipeline, staging through mpx and running phases), pick_provider (tag resolution with conflict-avoiding backtracking), with_sysroot_scope (the whole-config swap a sysroot-routed package runs under).
portformat.pm - pkg.conf parsing, both the new phase-based format and the legacy add.sh/del.sh one, inherit= class merging, pkg_use parsing, resolve_slot (static pkg_slot plus pkg_slot_use composition).
hooks.pm - run_hook: a hook_<phase>= config key, every executable file in a phase's hook directory (optionally filtered and ordered by a sibling hooks.conf), and custom events fired through $MP_HOOK, all through the same code path.
db.pm - the install db, one line per installed package (name, version, license, requested flag), and per-package file manifests, flock-guarded, written temp-then-atomic-rename.
util.pm - note, fail, ok (the message convention every command uses), slot_key and split_slot_key, pconf_lookup and pconf_section (the bare-name fallback a slotted canon's config needs, since "curl:ssl" doesn't exist as a section header until curl has actually been resolved with ssl on), order_manifest (the shared filter and topological-sort engine behind both patches.conf and hooks.conf's after= grammar). deliberately the dependency floor of mplib, it imports only mplib::config, so every other module can import it with no cycle risk.
version.pm - version comparison and the dependency-token grammar (>=1.2, =1.2.3, ~1.2, name:slot), used everywhere a version constraint appears.
the working directory layout (WD, default /usr/mp), what mp itself owns there:
db, one line per installed package
manifest/<canon>, one file per installed package's file list
pkgconf.conf, the mp-managed overlay behind the hold/mask/use commands
hooks/<phase>/, the default hook directory, one subdir per phase
<canon>/, a package's staging workspace while its install is in progress, gone again once the install finishes or is cleaned
sets/<name>.set, package sets ("mp install @name")
fetch-cache/<name>, "mp fetch"'s standalone cache, decoupled from the normal install lifecycle
sysroots/<name>/, an entire independent copy of all of the above, for that one named sysroot, its own db, manifest, hooks, staging area, fully isolated from the default root and every other sysroot
staging, and why a port's directory gets copied at all. install_pkg_body copies the port's own directory (pkg.conf, patches/, small text-only files, not whatever pkg_fetch= later downloads) into the staging workspace, writes each phase's shell body out there, then runs fetch, patch, build, and install in that copy, each phase a fresh process chdir'd into it. the copy exists so a build can freely write into that directory (a fetch: phase checks out or downloads real source there, patches apply to files there) without ever touching the ports tree itself. the tree stays exactly what "mp sync" (a plain git pull, per configured repo) expects to find, and two packages building at once never share a mutable directory. what actually consumes disk space during a build, the fetched source, build objects, is written directly into the stage dir by the fetch and build phases. it is never a second copy of anything that already lived in the ports tree, since a ports tree never bundles source, only the small pkg.conf and patches/ recipe that produces it.
repos. PORTS_DIR (default WD/repos) is the root every configured repo lives under. a single configured repo, the common zero-config case, clones directly to PORTS_DIR. once a second repo is configured, every repo moves to its own PORTS_DIR/repos/<name>, so no two repos' files ever land in the same directory on disk. see mp.conf.example for the full REPO_<name>= grammar.
mpx's own commands: stage and unstage (copytree and rmtree), snapshot (the before/after prefix diff install_pkg_body uses to learn what files a phase actually wrote), runsh (mpx.conf parsing plus the sandbox, then actually execing the phase script). each is its own small file behind one shared mpx.h. adding a whole new mp <command> is documented in extensions.btft; adding a new mpx backend command instead means a new function plus one line in mpx.c's dispatch table, the same shape as the six that already exist.