-
Notifications
You must be signed in to change notification settings - Fork 0
packages
A package is an account-shaped directory that accounts link to gain its verbs and subroutines:
mypkg/
PKG manifest: name / version / description / systems / dependencies
EXPORTS expression functions this package adds (NAME MINARGS MAXARGS)
NATIVE native extension build: C sources + optional pkg-config deps
BP/ source (mains become verbs, SUBROUTINEs become library)
VOC/ one text record per verb: V then CATALOG/<name>
CATALOG/ built verb executables (mkpkg output)
LIB/ built subroutine + extension libraries (mkpkg output)
Build with scripts/mkpkg.sh mypkg — main programs compile into
CATALOG/, and all SUBROUTINE sources bundle into one shared
library LIB/lib<name> (one dlopen, one artifact).
For a pure-BASIC package the BUILD-PKG <dir> verb builds it natively —
compiling BP/ through the runtime COMPILE primitive (no shell, no
mkpkg on PATH), so an installer can build a downloaded package with
only developer privilege, not the unrestricted tier a shell-out needs.
It compiles each SUBROUTINE into its own LIB/<name> library (the
CALL resolver loads every library in LIB/) and each main into
CATALOG/<name>; the package's shipped VOC/ is left untouched. A
package with a native (C) extension — a NATIVE manifest or
build-native.sh — needs a compiler, so BUILD-PKG reports
BUILD-PKG-NATIVE and defers to mkpkg; *.BP fallback source
directories are likewise mkpkg-only.
> LINK-PKG /path/to/git
linked /path/to/git
linked /path/to/cmd <- dependency, auto-resolved
> LIST-PKGS
git@1.0 ok /path/to/git requires cmd
cmd@1.0 ok /path/to/cmd
The PKG manifest is a plain record: attribute 1 name, 2 version, 3
description, 4 systems (the MV platforms the package targets — mvx,
unidata, universe, … — space-separated), 5 onward dependency names.
LINK-PKG refuses a package that declares systems but not mvx; a native
extension package (below) is inherently mvx-only. LINK-PKG links the whole
dependency closure — satisfying each dependency against already
linked packages, then a directory of that name beside the requiring
package, then $MVXPKGPATH — and refuses cleanly when one cannot be
found. A dependency name prefixed ? is optional: it is still
linked when it resolves (so the full package overrides), but its
absence is not an error — the requiring package ships a fallback under
the same names, so it runs standalone with no package manager present.
For example git depends on ?cmd and bundles a copy of cmd in
CMD.BP (compiled into libgit); a plain clone → mkpkg → LINK-PKG
of git works with no cmd package installed, and picks up the full
cmd automatically once it is. UNLINK-PKG refuses while another
linked package depends on the target.
Linked packages join verb resolution (after the local VOC, before the
system account) and their LIB/ joins CALL resolution. Package
verbs run in your account, on your data. A LINK-PKG takes
effect in the same session.
packages/cmd is a Cobra-style command framework:
CALL CMD.INIT("GIT", "work with the account's git repository")
CALL CMD.ADD("STATUS", "working tree status", "GIT.STATUS")
CALL CMD.ADD("LOG", "recent history", "GIT.LOG")
CALL CMD.RUN
CMD.RUN parses the sentence, dispatches to the handler subroutine
(via the indirect CALL @VAR), and generates help for help,
no-argument, and unknown-command cases. The git package is the
reference consumer — a GIT verb with subcommands in ~10 lines plus
one small handler subroutine each.
A package can ship subroutines written in C, not just BASIC: a C
function matching the subroutine ABI
(void mvx_sub_NAME(mvx_ctx *, int32_t argc, mv_value **argv)) is
CALLed exactly like a BASIC subroutine. Put the sources in the
package and build them into LIB/ with a build-native.sh script
(mkpkg runs it); link any native dependency into that library alone.
The git package does this — its git operations are libgit2-backed
native subroutines, so libgit2 burdens neither the runtime nor
programs that never use git.
Beyond statement subroutines, a package can add expression functions —
value-returning intrinsics usable in expressions, X = JSONENCODE(rec, spec),
exactly like a built-in. A native library declares them through the extension
ABI (runtime/include/mvx_ext.h):
static void ext_jsonencode(mvx_ctx *ctx, mv_value *ret, int32_t argc,
mv_value **argv) { ... write ret ... }
static const mvx_extfn fns[] = { {"JSONENCODE", 2, 2, ext_jsonencode}, ... };
static const mvx_ext ext = {"json", 2, fns};
const mvx_ext *mvx_ext_entry(int abi) { return abi == MVX_EXT_ABI ? &ext : 0; }The package declares the same names in EXPORTS (NAME MINARGS MAXARGS, one
per line) so the compiler accepts them, and a NATIVE manifest (source
files, plus optional pkgconfig: <libs> lines) that mkpkg builds into
LIB/libmvxext_<pkg>. Runtime symbols (mv_*, mvx_*, the core mapper) resolve
from the host at load, like the storage drivers. The functions are also callable
as statements (CALL NAME(...)), and a package shipping only a prebuilt
libmvxext_<pkg> with no sources is binary-only.
Inclusion is configuration, not a flag. A package is available to compile
against and run once it is either installed into the toolchain or linked
into an account with LINK-PKG (the compiler reads that account's linked
PACKAGES). Two CMake lists install into the toolchain, and they differ in
where the package comes from:
MVX_PACKAGES |
packages in the mvx tree, built from source at build time (http) |
MVX_BUNDLED_PACKAGES |
published packages, downloaded already built at install time (git) |
MVPKG is always installed and is in neither list: it is how everything else is installed afterwards, so it comes with the toolchain the way the toolchain's own verbs do.
Versions are not pinned in the build: the install asks, offering each
package's latest stable release first and taking it automatically when nobody
is at the terminal. MVX_PACKAGE_VERSION_mvpkg and MVX_PACKAGE_VERSION_git
pin an exact version instead — see Getting started for the
prompt. Each download is checked against the checksum published beside it.
MVPKG publishes Linux x86-64 and arm64 builds, git x86-64 only; where a package
has no build, the install warns and carries on without it.
packages/http is a native extension that adds an HTTP client to the language.
It lives in the mvx tree and is installed by default (MVX_PACKAGES defaults to
http), so the functions are always available:
X = HTTPGET(url) ;* GET url -> the response body on a 2xx status,
;* "" on a non-2xx status or a connection error
S = HTTPGETFILE(url, path) ;* GET url, writing a 2xx body to `path`;
;* returns the HTTP status (-1 connect, -2 write)
Plain HTTP/1.1 over POSIX sockets — no external dependency — with chunked
transfer decoding. HTTPS is a follow-up. HTTPGET composes with JSONDECODE
for JSON APIs, and the pair is the transport the MV package manager uses to
reach a registry and pull release tarballs.
CATALOG BP MYSUB for a SUBROUTINE source builds LIB/MYSUB in
the account instead of a verb. CALL resolves at runtime: symbols
already in the program, then the account's LIB/, each linked
package's LIB/, then the system LIB/. The subroutine ABI is
frozen, so libraries built at different times interoperate.
A package may ship standalone OS commands in a bin/ directory —
executables that run from the shell, not from TCL. mkpkg.sh links
whatever it finds there onto the dev PATH (build/bin, beside mvx), and an
install copies them into <prefix>/bin. The git package uses this to ship
mvx-git (built from C by its build-native.sh), which is why an installed
toolchain has mvx-git beside mvx. See Version Control.