Project layout
A mach project is a tree of .mach files under a source directory, anchored by a mach.toml manifest at the root. The project's id is the root of every module path the project exposes.
Source files
Source files use the .mach extension. Two conventional entry files name a project's surface:
main.mach- executable entry.lib.mach- library entry. Exposes code for other projects to import; produces no executable.
These names are convention only: the compiler never looks for a function called main. An entry is simply the source file a [bin.*] or [lib.*] artifact names with its entry key; what marks a function as the program's entry point is its exported symbol and signature, covered in Hello world. The artifact table fixes the role - a [bin.*] links an executable, a [lib.*] produces a library. A project may carry both files.
The project root
Every project has a mach.toml at its root. The compiler finds it by walking up from the working directory until one is seen, so any subcommand run inside the tree resolves the same manifest.
[project]
id = "myproj" # root of every module path
version = "0.1.0"
src = "src" # source dir (default "src")
[target.linux]
isa = "x86_64"
os = "linux"
abi = "sysv64"
[bin.app]
entry = "main.mach" # executable entry, relative to src
Only id and version are required. src (default "src") is where module paths resolve; vendored dependencies live under dep (default "dep"). The full schema - targets, profiles, artifacts, dependencies, output templates - lives on the Manifest page.
Files map to module paths
The path separator is .. A file at <src>/foo/bar.mach in a project with id = "myproj" is reachable as myproj.foo.bar.
myproj/
mach.toml
src/
main.mach # myproj.main
foo/
bar.mach # myproj.foo.bar
There is no this. self-prefix. Within a project, modules always reference each other by their full project-rooted path, so every reference is syntactically uniform no matter where it appears.
Surface and split modules
A file foo.mach may co-exist with a directory foo/. The file is the surface module - the public face of foo. The directory holds split implementations that the surface loads and re-exports.
src/
foo.mach # surface: myproj.foo
foo/
a.mach # split: myproj.foo.a
b.mach # split: myproj.foo.b
The surface loads each split with use and re-exports its public symbols with fwd. Consumers use myproj.foo and reach symbols through the surface; they never name the split files directly.
use myproj.foo.a;
fwd a.X; # re-export a's public symbol through the surface
Topical splits
Organize a large module by topic. Every split is forwarded unconditionally, keeping one logical module spread across readable files.
Multiplatform splits
Keep one implementation per target and select it with $if on $mach.build.os or $mach.build.arch, so the surface forwards only the split that matches the build.
Importing a project by id
A one-segment use/fwd path equal to a resolvable project id - a dependency's id or the current project's own - resolves to that project's declared [project].module. A library that sets module = "glfw.mach" is imported by its id instead of by the full path to its surface file.
# in glfw's manifest
[project]
id = "glfw"
module = "glfw.mach" # the surface a bare use glfw binds
# in a consumer
use glfw; # binds glfw's [project].module
A bare import of a project that declares no module is a resolution error naming the fix: import a full path, or add a module key. A declared module that names no file is a manifest error at build start. Longer paths are unaffected, so this is purely additive.
See also
- Modules - how files map to dotted module paths
- Manifest - the full
mach.tomlschema - Dependencies - vendored deps under the
depdir - Visibility -
pubandfwdre-exports