aeaether
$docs / build

Aether install layout

When you install Aether (make install or ./install.sh), the toolchain + headers + sources + module descriptors land under a single $PREFIX (default ~/.aether):

$PREFIX/
├── bin/                          # toolchain binaries
│   ├── ae                        # the user-facing CLI (`ae build`, `ae run`, …)
│   ├── aetherc                   # the compiler (lower-level; ae usually wraps)
│   └── aether-lsp                # language server (install.sh only, if built;
│                                  # `make install` does not install it)
│
├── lib/aether/
│   └── libaether.a               # prebuilt static archive, link target for downstream
│                                  # programs / libraries that compile against the runtime
│
├── include/aether/               # public headers, mirrors source-tree layout
│   ├── runtime/
│   └── std/
│
└── share/aether/
    ├── MANIFEST                  # authoritative list of link-suitable .c files (#329)
    ├── runtime/                  # runtime C sources (the actor scheduler, memory,
    │                              # panic/try-catch, sandbox, etc.)
    ├── std/                      # stdlib sources + module.ae descriptors
    └── contrib/                  # contrib module.ae descriptors + headers
                                   # (matching libaether_<x>.a archives come from
                                   # `make install-contrib`; this dir alone gives
                                   # downstream `import contrib.X` resolution
                                   # without the .a archive)

How downstream tools should consume the install

Two consumption shapes, in priority order:

  1. Link against libaether.a (recommended). Every Aether runtime + stdlib .c file that ships compiles into this single archive. The canonical recipe for the include + link flags is:
   gcc your.c $(ae cflags) -o your-program

ae cflags resolves to the right -I<prefix>/include/aether/... plus -L<prefix>/lib/aether -laether plus the OS-level deps (-pthread -lm, plus -lssl -lcrypto -lz -lnghttp2 -ldl if the build picked them up at install time). It works identically for in-tree dev builds, ~/.aether user installs, and /usr/local system installs.

Do not hand-craft -L / -l: libaether.a lives at <prefix>/lib/aether/libaether.a (under the aether/ subdir, not flat under <prefix>/lib/), so a bare -laether will fail to resolve without the explicit -L<prefix>/lib/aether. tools/ae.c itself uses this same path, see cmd_cflags for the construction.

ae build is tolerant of a package that ships the archive flat at <prefix>/lib/libaether.a instead: it tries the canonical nested path first and falls back to the flat one before resorting to the compile-from-source path below (#959). The nested layout is still the canonical one install.sh and make install produce.

  1. Compile from source, falling through to the .c files in share/aether/. This is what ae build does when libaether.a isn't found. It's also what tooling like aetherBuild's aeb-link does.

In this mode, read share/aether/MANIFEST to discover which .c files to compile. The MANIFEST is the authoritative list, one path per non-comment, non-empty line, paths relative to share/aether/. Naive find runtime -name '*.c' walks would pull in benchmarks, orphaned poller hubs, and other source that isn't link-suitable.

   # Read MANIFEST + build the link command:
   cd $PREFIX/share/aether
   src=$(grep -v -E '^(#|$)' MANIFEST)
   gcc your.c $src -I $PREFIX/include/aether/runtime -I $PREFIX/include/aether/std \
       -pthread -lm -lssl -lcrypto -lz -lnghttp2 \
       -o your-program

MANIFEST format

# Aether MANIFEST, link-suitable C source files for
# downstream consumers compiling against the runtime/std
# source tree (e.g. aetherBuild's aeb-link). Generated by
# the Makefile; do not edit by hand. Regenerated on every
# `make stdlib`.
#
# Format: one path per non-comment, non-empty line. Paths
# are relative to this install root (share/aether/ on a
# system install; the repo root in the source tree).
# Lines starting with '#' are comments.

# Runtime sources:
runtime/scheduler/multicore_scheduler.c
runtime/scheduler/scheduler_optimizations.c
runtime/aether_runtime.c
…

# Standard library sources:
std/string/aether_string.c
std/http/server/h2/aether_h2.c
…

The MANIFEST is generated from RUNTIME_SRC + STD_SRC in the Makefile so it always matches what we actually link into libaether.a. The build target is build/MANIFEST; both make install and install.sh copy it into $PREFIX/share/aether/MANIFEST.

What does NOT ship to the install

make install and install.sh deliberately trim a few directories that aren't link-suitable:

  • runtime/examples/ standalone benches with their own main(). Compiling these into a downstream link would produce duplicate- main link errors.
  • runtime/io/ orphaned poller dispatch hub plus four platform variants from an earlier design iteration. The active poller variants live under runtime/scheduler/aether_io_poller_*.c; the orphaned dir would produce missing-typedef errors if a downstream tool tried to compile it.
  • contrib/<X>/{tests,benchmarks,example_*.ae,test_*.sh,build.sh, ci.sh,*.m}, contrib source-tree noise. .c files are also dropped EXCEPT the host-language bridges at contrib/host/<lang>/aether_host_<lang>.c. The bridges ship as source because make install does NOT also build/install libaether_host_<lang>.a only make install-contrib does that, and it's a separate opt-in step (because each bridge needs the matching dev library: python3-dev, liblua5.4-dev, etc.). Downstream apps that import contrib.host.<lang> from a plain make install tree compile the bridge as source into their own build. The matching libaether_<X>.a archives carry the C side for everything else; only module.ae descriptors + public headers ship under share/aether/contrib/.

Building against an installed Aether: required defines

Apps that #include runtime/std headers and link against libaether.a from a plain make install tree must compile with the same preprocessor defines the install was built with, otherwise the header-side macros and the library-side symbols disagree. The flag that matters:

  • -DAETHER_HAS_SANDBOX, gates aether_sandbox_check(). When defined, runtime/aether_sandbox.h provides a static inline aether_sandbox_check() that consults the global _aether_sandbox_checker (defined in libaether.a); when undefined, the header replaces it with a no-op macro that always returns 1. There's no linker error on skew, the function is inline in both branches, so a build that compiles the header without the flag silently loses sandbox enforcement even when linked against a sandbox-enabled library. The shipped libaether.a is always built with this flag (see CFLAGS in the Makefile, which carries -DAETHER_HAS_SANDBOX), so downstream consumers should compile with it too.
  • Any future AETHER_HAS_* flags introduced for optional runtime subsystems will follow the same pattern.

The version stamp at lib/aether/VERSION catches the ae-vs-libaether version skew separately; the define skew is its own thing and silent unless a symbol is actually missing.

The MANIFEST never references trimmed paths, the regression test at tests/integration/install_manifest/ verifies this on every build.

Option C alternative (status quo + docs)

Issue #329 considered three options for cleaning up the install layout:

  • (A) Drop runtime/std sources entirely. Header-only include/ + prebuilt libaether.a + module.ae resolver descriptors. Cleanest, but breaks the source-fallback path in tools/ae.c that runs when libaether.a is missing (e.g. cross-built / partial installs).
  • (B) Ship MANIFEST (the shipped choice). Conservative, keeps the source-fallback path working as a backstop, gives downstream tools an authoritative list, no breakage.
  • (C) Status quo + docs only. Low-touch, but doesn't help consumers who write naive find walks and trip on runtime/examples/ / runtime/io/.

Chose B. If a future downstream consumer needs the static-only shape, Option A can land additively (the MANIFEST stays useful either way).