Modules and packages
Up until now every example has been one file. Real codebases grow past that fast. This chapter is everything you need to organize Glide code in any size from "small CLI" to "service with five libraries."
Modules: one file, one namespace
In Glide, a file is a module. The module's name comes from the path under src/ with the .glide stripped:
hello/
glide.glide
src/
main.glide ← module: main
storage/
sqlite.glide ← module: storage::sqlite
memory.glide ← module: storage::memory
To use something defined in another module, import it:
import storage::sqlite::*; // bring in everything pub from that file
import storage::memory::*;
Read the path with :: as the separator. The ::* at the end means "bring all public items into scope so I can use them by their short name."
Public vs private
By default, anything defined in a module is private — only visible inside that file. To export, prefix with pub:
pub struct Db {
handle: *void,
}
pub fn open(path: string) -> !Db {
// ...
}
fn _internal_helper() -> i32 {
// no pub: this stays private to this file
return 0;
}
From main.glide:
import storage::sqlite::*;
fn main() -> i32 {
let r: !Db = open("data.db"); // works — open is pub
return 0;
}
Different ways to import
// Import everything pub from a module:
import storage::sqlite::*;
// Import the module under its short name, call things qualified:
import storage::sqlite; // then: sqlite::open("data.db")
// Import just one thing by name:
import storage::sqlite::{open};
The * form is the most common. The qualified-name form is nice for resolving collisions when two modules define the same name.
The glide.glide manifest
Every project has a manifest at its root. It's not a config file — it's a Glide value:
let manifest: Package = Package {
name: "myapp",
version: "0.1.0",
description: "Example application",
author: "Your Name",
license: "MIT",
repository: "https://github.com/you/myapp",
bin: "src/main.glide", // entry point
deps: vec_of(
Dep::git("discord_lib", "https://github.com/you/discord-lib", "v0.1.0"),
Dep::path("local_lib", "../local-lib"),
),
};
The CLI reads the literal fields. The interesting ones:
- `name` — the package name. Has to match the directory name and the lib entry filename (more on that below).
- `version` — bumped manually on release; consumers pin to a specific revision rather than this version.
- `bin` — the source file with
fn main(). For a library package, set this to"". - `deps` — a
vec_of(...)list ofDepvalues. Two kinds:Dep::git(name, url, rev)andDep::path(name, local_path).
Adding a dependency
You can edit glide.glide by hand, or use the CLI:
glide add discord_lib github.com/you/discord-lib v0.1.0
That appends the dep to your manifest. Then download it:
glide fetch
glide fetch pulls each dep into a content-addressed cache (~/.glide/cache/<sha256>) and creates a symlink at glide_modules/<dep-name> pointing to it. You commit your glide.glide and glide.lock to git; you do not commit glide_modules/ (the install script regenerates it).
Use the dependency by importing its package name:
import discord_lib::*;
// or, if its symbols live in submodules:
import discord_lib::gateway::*;
import discord_lib::rest::*;
Publishing a library
Want others to depend on your code? Three steps:
- Structure: create the directory with this layout (use
glide new mylib --lib):
mylib/
glide.glide # name: "mylib", bin: ""
src/
mylib.glide # entry point — same name as the package
# ... more files here, importable as mylib::other_file
The entry-file's name has to match the package name. That's how import mylib; finds the right file.
- Publish: push to GitHub and tag a version:
git tag v0.1.0
git push origin v0.1.0
- Consume: anyone can now add your library:
glide add mylib github.com/you/mylib v0.1.0
glide fetch
Building for production
When you're ready to ship, glide build --release makes the optimized binary:
glide build --release
# → ./build/myapp (or myapp.exe on Windows)
That binary is self-contained: it doesn't need Glide installed on the target machine. Copy it to your server, run it, done.
For cross-compiling to another OS without leaving your machine:
glide build --release --target=x86_64-linux-gnu # Linux
glide build --release --target=aarch64-apple-darwin # macOS ARM
glide build --release --target=x86_64-windows-gnu # Windows
The bundled zig cc handles the cross-build — no separate toolchain to install.
Reading the dep tree
glide.lock tracks the exact revision (commit SHA + content hash) of each dependency you've fetched, so a fresh clone resolves to the same code. Commit it to git just like package-lock.json or Cargo.lock.
Inspect what's actually pulled in:
ls glide_modules/ # top-level deps
ls glide_modules/<dep> # source of one dep
glide_modules/ is a flat list of symlinks into the content-addressed cache. Two projects depending on the same revision of the same library share one underlying copy on disk.
A realistic project layout
What an HTTP service looks like by the time it's a few thousand lines:
myservice/
glide.glide # name, version, deps
glide.lock
src/
main.glide # fn main() — wires everything together
config.glide # env parsing, validation
handlers/
health.glide
users.glide
posts.glide
storage/
pool.glide # connection pool
queries.glide # SQL
middleware/
auth.glide
logging.glide
book/ # this site, perhaps
static/
tests/
glide_modules/ # not committed; populated by glide fetch
sqlite/
redis/
auth_jwt/
build/ # not committed; output of glide build
Inside main.glide you'd see:
import handlers::health::*;
import handlers::users::*;
import handlers::posts::*;
import middleware::auth::*;
import middleware::logging::*;
import sqlite::*;
fn main() -> i32 {
// ... wire up routes, start server
return 0;
}
Recap
Where to next
That's the core language and its tooling. The remaining chapters put it to work: the next one — A tour of the standard library — surveys the batteries Glide ships with, the APIs you'll actually call day to day.
After that: building an HTTP server, testing and benchmarks, and FFI and escape hatches.