Proposed .aea Module Artifacts
Problem
make install currently gives downstream users two different kinds of stdlib material:
$(PREFIX)/lib/aether/libaether.a: precompiled C runtime and C-backed stdlib support.$(PREFIX)/share/aether/std/.../module.ae: Aether-authored stdlib modules installed as source.
That means top-level C-backed modules such as std.cryptography are already available through the installed archive, while Aether-authored modules such as std.cryptography.md2, std.cryptography.sha3, std.cryptography.blake2, and the KDF/hash submodules are compiled again by each downstream application that imports them.
This is functional, but it is not the experience users expect from a first-class installed standard library. Java users do not think about whether a package came from source or from a .jar; the import experience is the same. Aether should aim for that same level of artifact transparency.
Goal
Compiled Aether stdlib modules should behave as if their sources were co-located with the importing program.
For example:
import std.cryptography.md2
should mean the same thing whether the resolver uses:
- a local source tree,
- an installed
share/aether/std/cryptography/md2/module.ae, or - an installed compiled module artifact.
The artifact is an implementation detail and cache boundary, not a new import surface.
Non-goal
Do not make this a flattened C-grade experience.
An artifact such as:
$(PREFIX)/lib/aether/std.cryptography.md2.aea
must not degrade the user-facing model into "generated C plus a linker archive". Users should not need to know generated C symbol names, object file layout, or link ordering to import a normal Aether module.
Desired Developer Experience
The import path remains the API:
import std.cryptography.md2
import std.cryptography.sha3
import std.cryptography.pbkdf2
Diagnostics remain Aether diagnostics:
- module names are reported as
std.cryptography.md2, not generated C files; - exported functions are reported with their Aether signatures;
- source locations map back to the original
.aemodule where possible; ae help, doc generation, and import resolution see the same exported surface they would see from source.
Build tools should not have a separate "compiled stdlib import" path. The normal module resolver should choose the best compatible backing artifact.
Proposed Artifact Shape
Use a first-class compiled Aether module archive, tentatively .aea.
One possible installed layout:
$(PREFIX)/lib/aether/modules/std/cryptography/md2.aea
$(PREFIX)/lib/aether/modules/std/cryptography/sha3.aea
$(PREFIX)/lib/aether/modules/std/cryptography/pbkdf2.aea
The exact path is open, but it should preserve module hierarchy rather than flattening everything into opaque filenames.
Each .aea should contain structured metadata and compiler-owned payloads, for example:
module.json
typed-ir.aeir
objects/<target-triple>.o
docs.json
source.map
Where:
module.jsonrecords module name, compiler version, Aether ABI version, target compatibility, build flags, imports, exports, and source hash.typed-ir.aeiris a typed Aether IR payload, not generated C as the public contract.objects/<target-triple>.ois an optional native object cache for targets where the artifact producer built one.docs.jsonrecords exported docs and signatures forae helpand docgen.source.mapmaps diagnostics and generated code back to original Aether source locations.
The object cache is allowed to be disposable. The typed IR and metadata are the important first-class module boundary.
Resolver Behavior
Suggested precedence:
- local project source;
- explicit
--libsource or artifact paths; - compatible installed
.aeaartifact; - installed source fallback under
$(PREFIX)/share/aether/std/.../module.ae.
This preserves today's source-based behavior while allowing an installed toolchain to skip recompiling stdlib Aether modules when a compatible artifact exists.
Compatibility checks should include at least:
- compiler version or IR format version;
- Aether ABI version;
- target triple or target family;
- relevant compile flags and feature gates;
- dependency artifact hashes;
- stdlib/toolchain version.
If the artifact is missing or incompatible, the resolver should fall back to source when source is installed.
Why This Matters
The current model pushes a compilation chore onto downstream applications. Every clean build of an application that imports Aether-authored stdlib modules must recompile those modules into the application.
That has costs:
- slower downstream clean builds;
- repeated work across applications;
- less obvious installed artifact boundaries;
- a split mental model where C-backed stdlib feels prebuilt but Aether-backed stdlib feels source-distributed.
A first-class .aea format fixes that without changing imports.
Open Design Questions
- What typed IR is stable enough to serialize across patch releases?
- Is
.aeaa per-module artifact, a package-level artifact, or both? - Should native object caches be mandatory or opportunistic?
- How much whole-program analysis currently assumes source ASTs are available?
- How are generic instantiations, closures, builders, and effect/capability checks represented in the artifact?
- How does
--emit=libplus--with=fs,net,oscapability gating interact with precompiled artifacts? - How does artifact invalidation work for local overlays, patched stdlib trees, and cross-compilation?
Success Criteria
This feature is successful when:
import std.cryptography.md2works identically from source or.aea;- downstream applications do not need to recompile installed stdlib Aether modules on every clean build;
- diagnostics and docs remain Aether-native;
- installed source remains a fallback and debugging aid;
- build tools can treat
.aeaas a normal module-resolver result, not a special linker-only input.