Packages with Dawn
Dawn is the package tool for Dusk, and since 1.14.0 it is backed by a real package system. There is still no central registry: a package is a git repository with a package.dawn manifest at its root, a version is a git tag pinned to a commit, and a project is any directory that holds a package.dawn of its own. This guide walks you through creating a project, declaring a dependency, importing through it, and understanding the manifest, the lock file, and the local cache that make a build repeatable and offline.
Who does what
Section titled “Who does what”The design draws a clean line between the tool and the compiler, and it is worth holding onto because it explains everything that follows.
Dawn owns the network. It resolves the references you write into exact commits, writes those commits into a lock file, and checks the code out under dawn_modules/. The compiler owns the build. It reads the manifest, the lock, and the checkouts on disk, and it never opens a socket. The upshot is that a machine which has fetched a project’s dependencies once builds it offline forever, and a machine that has not fetched yet is told exactly which command to run rather than left guessing.
Creating a project
Section titled “Creating a project”dawn init [name] writes a starter package.dawn and adds dawn_modules/ to your .gitignore, since the checkouts under it are generated and never committed. From there a project is just a directory with a manifest, a root source file, and (once you have dependencies) a lock file beside the manifest.
myapp/ package.dawn the manifest, committed dawn.lock the resolved commits, committed main.dusk the root source file dawn_modules/ generated checkouts, gitignoredThe manifest
Section titled “The manifest”package.dawn is a directive file, not dusk source. Dawn reads it line by line. A // comment is allowed only at an argument boundary, and quoted values use double quotes with no escapes. Every directive begins with @, and there are seven of them.
@package myapp@version "0.3.0"@dusk 1.14
@root main.dusk
@require maybe github.com/dusk-pkg/maybe v1.2.0@require greet github.com/dusk-pkg/greet main
@link "m"@csource "runtime/shim.c"@package <name>is the package’s own name. It may hold letters, digits,_, and-only.dusk docand dawn’s own output use it, anddusk build --libuses it as the stem of the generated header.@version <text>is informational: it is the tag dawn suggests when you cut a release. Dawn never parses or compares it.@dusk <major.minor>names the oldest compiler the package builds with. It is compared numerically, and an older compiler refuses the build by name, for examplepackage.dawn asks for dusk 1.99, this is 1.15.0.@root <path>is the entry and library root, relative to the manifest. It defaults tomain.dusk, and thensrc/main.dusk, when you leave it out.@require <alias> <source> <ref>declares one dependency per line: the alias you will import through, where the package lives, and which tag, branch, or commit to pin.@link <value>and@csource <path>are the package-wide forms of the source-file directives of the same name (see source files). They feed the link line for the whole package rather than a single file.
The grammar is strict on purpose. Each directive has a fixed arity, and a miscount is named: @require takes 3 arguments (alias source ref), got 2. A single-valued directive written twice is refused with duplicate @package, and a directive dawn does not know is refused with unknown directive '@nope'. The lock file uses @lock lines and only those, so @lock never appears in package.dawn.
Sources, references, and aliases
Section titled “Sources, references, and aliases”A source is either the short host/owner/repo form, where https is assumed, or a full URL beginning with https://, git@, ssh://, or file://. A reference is a tag, a branch, or a commit. An alias is an ordinary identifier, with std and dawn_modules reserved so a dependency can never shadow the standard library or the checkout directory.
Adding and locking a dependency
Section titled “Adding and locking a dependency”You can write a @require line by hand or let dawn append one for you:
dawn add maybe github.com/dusk-pkg/maybe v1.2.0Declaring a dependency is only half of it. Before the compiler will build, every requirement needs a line in dawn.lock, which is generated and committed alongside the manifest. dawn get resolves each reference to a commit and writes one @lock <alias> <source> <ref> <commit> line per requirement, in declaration order. dawn update re-resolves the lock when you want to move a pin. A requirement with no lock line stops the build with 'maybe' is not locked; run dawn get, so the fix is always a single named command.
Each checkout lands flat: every dependency in the graph, however deep, sits at dawn_modules/<alias>/ as a detached checkout at its locked commit, next to a one-line stamp at dawn_modules/<alias>/.dawn.
Importing through a dependency
Section titled “Importing through a dependency”Once a dependency is locked and fetched, you import from it exactly the way you import a local module or a stdlib module: the alias the manifest declared, then an ordinary dotted path.
@import maybe.maybe // the maybe module inside the maybe package@import greet.greet_line // alias + one name: a symbol the root file exportsThe first form reaches a module file inside the dependency and you call through its qualified name. The second, an alias plus a single name, imports a symbol that the dependency’s root file exports. The general shape is alias.file.symbol, the same walk of directories, then files, then symbols that every dotted path takes.
Exports are import-scoped
Section titled “Exports are import-scoped”Added in 1.14.1, a dependency’s exports are import-scoped, and this is what lets two packages that both export a parse coexist. Inside a dependency’s own files every exported name is internally renamed <name>__<alias>, so a file sees a dependency’s export bare only through its own @import of it, and alias.name() reaches it from anywhere. A local declaration of the same name shadows the dependency’s export.
What a file may spell bare, then, is its own declarations, every other file of the project, the std modules any file in the project imported, and the exports each of that file’s own @imports reached. Some names never take the alias suffix, because something outside dusk resolves them: export "C" functions and foreign block declarations (the linker resolves those C symbols), struct fields, methods, and enum variants (you reach those through a typed value), an exported bind or unit (the shape do-notation relies on), and builtins and primitive type names.
Resolving the graph
Section titled “Resolving the graph”When more than one dependency pulls in the same package, dawn settles the graph before it writes the lock, and a conflict names both manifests so you can see where the disagreement came from.
- The same source at the same ref, required by two dependents, is fetched once and shared.
- One alias naming two different sources is refused.
- One alias at two different refs takes the root manifest’s pin, treating your top-level
@requireas the override, or is refused when the root does not pin it. - One source at one ref reached under two different aliases is refused.
What the compiler sees
Section titled “What the compiler sees”The package system is additive. The compiler discovers a manifest by walking upward from the directory of the root source file to the filesystem root, and the first package.dawn it meets wins. A file with no manifest anywhere above it compiles exactly as it did before 1.14.0, so nothing you already build is affected until you opt in with a manifest.
When you run dusk build, dusk run, dusk check, or dusk ir with no file argument inside a project, the compiler uses the manifest’s @root. And because a dependency’s files register under dawn_modules/<alias>/<path>, always relative to the manifest and never as an absolute path, two machines with the same lock emit identical IR. A dependency’s own @dusk floor is enforced too, since 1.14.1: a checkout that asks for a newer compiler than yours is refused with dawn_modules/<alias>/package.dawn: package.dawn asks for dusk 9.9, this is 1.15.0.
Publishing a package
Section titled “Publishing a package”To publish, you make your project a package other projects can require. Give it a package.dawn with an @package name, set @dusk to the oldest compiler you support, and point @root at the file whose exports form your public surface. Push the repository somewhere reachable and tag a release; the tag is the reference a consumer writes in their @require, and @version is where you record the tag you mean to cut. Because the source is the package, there is nothing further to register or upload.
Migrating from the old git import
Section titled “Migrating from the old git import”Before 1.14.0, an external package was pulled in with a quoted git path, for example @import "example.com/user/repo/mod". Inside a project that has a manifest, that form is now refused with '...' is a url import; declare it with @require in package.dawn. Outside a project it still resolves in this release, though nothing fills that old cache anymore. The migration is mechanical: add a @require for the repository at the tag you want, then respell the import through the alias you gave it. The quoted form is on its way out, and it survives through 1.15.1.
See also
Section titled “See also”- The dawn package tool: the command reference, the cache, and offline behavior
- Source files:
@importsyntax and how modules resolve - The dusk CLI: the compiler commands dawn drives