The binary produced by
mcpp buildonly runs on the local machine —— both the loader and the RUNPATH point into~/.mcpp/. To distribute it to other machines or deploy it to a server, usemcpp packto produce a self-contained tarball.
Distribution is two orthogonal choices:
- libc / static — a build-target property:
--target …-linux-gnu(glibc) vs--target …-linux-musl(musl, static).--target …-muslimpliesstatic. - bundling depth — a pack property: how much of the shared-lib closure
travels with the artifact. This is what
--modeselects.
| Mode | Host must provide | Size | Use case |
|---|---|---|---|
system |
every .so (incl. third-party) |
smallest | .deb/.rpm, same-distro fleet (pkg manager declares deps) |
vendored (default) |
libc / libstdc++ / loader | +a few MB | Mainstream distros (Ubuntu 22+, Debian 12+, RHEL 9+) |
self-contained |
nothing | +30–50 MB | Any Linux incl. older glibc; bundles closure + run.sh wrapper |
static |
nothing (single file) | +5–10 MB | musl; any Linux x86_64, Docker scratch, Alpine |
How to choose:
- Distro packages (
.deb/.rpm) or same-distro internal deploy →system - Desktop / server releases for mainstream distros →
vendored(default) - Cross-distro / older glibc (legacy CentOS, Kylin) →
self-contained - Single portable file, no host deps →
static
Canonical names are shown above. The old names remain permanent aliases:
bundle-project = vendored, bundle-all = self-contained. Tarball-name
suffixes are a frozen wire format (consumed by install.sh) and do not
follow the rename: vendored → no suffix, self-contained → -bundle-all,
static → -static, system → -system.
mcpp pack # vendored by default
mcpp pack --mode system
mcpp pack --mode static
mcpp pack --mode self-contained # alias: --mode bundle-all
mcpp pack --target x86_64-linux-musl # equivalent to --mode static
mcpp pack --format dir # output as a directory, no tarball
mcpp pack -o myapp.tar.gz # filename only: lands at target/dist/myapp.tar.gz
mcpp pack -o /abs/path/myapp.tar.gz # includes a directory: output to the literal pathWhen -o is given a bare filename, the output is placed under target/dist/;
when it includes a directory (relative or absolute), the literal path is used.
For the full set of options, see mcpp pack --help.
The tarball contents are wrapped in a single top-level directory whose name
matches the tarball filename (minus the .tar.gz) —— this way both a GUI
"right-click extract" and a command-line tar -xzf yield the same
self-contained directory, instead of scattering the contents across the current
path.
target/dist/myapp-0.1.0-x86_64-linux-musl-static.tar.gz
└── myapp-0.1.0-x86_64-linux-musl-static/
├── bin/myapp ← fully static ELF (no PT_INTERP / RUNPATH)
├── myapp ← top-level entry point (thin shell wrapper, run ./myapp directly)
├── README.md ← copied automatically from the project root
└── LICENSE
target/dist/myapp-0.1.0-x86_64-linux-gnu.tar.gz
└── myapp-0.1.0-x86_64-linux-gnu/
├── bin/myapp ← dynamically linked, RUNPATH=$ORIGIN/../lib
│ PT_INTERP=/lib64/ld-linux-x86-64.so.2
├── lib/
│ ├── libcurl.so.4 ← project third-party dependency
│ ├── libssl.so.3
│ └── ...
├── myapp ← top-level entry point
├── README.md
└── LICENSE
The skip list follows
PEP 600 / manylinux2014 ——
base libraries such as libc, libm, libstdc++, libgcc_s, and
ld-linux-* are assumed to already exist on the target system and are not
bundled into the tarball.
target/dist/myapp-0.1.0-x86_64-linux-gnu-bundle-all.tar.gz
└── myapp-0.1.0-x86_64-linux-gnu-bundle-all/
├── bin/myapp
├── lib/
│ ├── ld-linux-x86-64.so.2 ← complete loader and libc
│ ├── libc.so.6
│ ├── libstdc++.so.6
│ ├── libgcc_s.so.1
│ └── ...project dependencies
├── myapp ← one of two entry points
├── run.sh ← the other entry point (identical contents)
├── README.md
└── LICENSE
With -o foo.tar.gz, the top-level directory name also becomes foo (the
package name and directory name always stay in sync).
The ELF specification forbids PT_INTERP from using $ORIGIN, so in
bundle-all mode the loader is invoked by absolute path through run.sh (and
the top-level wrapper of the same name):
exec "$here/lib/ld-linux-x86-64.so.2" --library-path "$here/lib" "$here/bin/myapp" "$@"Packaging behavior is configured via the [pack] section in mcpp.toml. The
common fields are:
[pack]
default_mode = "static" # default mode when --mode is omitted
include = ["share/**", "config/*.toml"] # extra files to bundle
exclude = ["debug/**"]
# Fine-tune the bundle-project filtering policy
[pack.bundle-project]
also_skip = ["libcustom.so"] # libraries assumed to exist on the target system
force_bundle = ["libfoo.so"] # bundle even if matched by the PEP 600 listThe static mode additionally requires a musl toolchain configured under
[target.<triple>]; for the full setup, see the mcpp.toml in
examples/03-pack-static.
macOS dylib, Windows DLL, and distribution formats such as .deb / .rpm /
AppImage are still on the roadmap. This document evolves alongside the
mcpp pack implementation; for the latest options, refer to
mcpp pack --help.