LEVIATHAN v962456e · 962456eee1

trident

The manifest: trident.toml

Every key of trident.toml, how sources and assets are listed, and the three ways a project can choose its entry point.

since 0.1.0-alpha.1linux

Description

A project is described by a file named trident.toml in the project's root directory. The name is fixed. trident reads it; the compiler never does. The format is a small subset of TOML: string values, lists of strings (which may continue over several lines), true and false, # comments, and repeated [[dep]] tables.

name    = "app"
entry   = "main.lev"                  # a function name, or a file ending in .lev
sources = ["*.lev", "models/*.lev"]   # globs expand alphabetically
assets  = ["views/**", "schema.sql"]  # files a comptime import() may read
version = "0.1.0"                     # optional
out     = "app"                       # optional

[[dep]]                               # repeat for each dependency
path    = "../jsonlib"
as      = "Json"
version = "1.0.0"
dev     = false
Key Meaning
name The project's name. It is also the default name of the executable.
entry Where the program starts: a function name, or the name of a source file ending in .lev. Optional.
sources The source files, as paths relative to the manifest. Required, and it must list at least one file.
assets Files that comptime import() calls may read. Optional.
version The project's own version, used when the project is published. Optional.
out The name of the executable that trident build writes. Optional; the default is name, or a.out when there is no name either.
[[dep]] One dependency each; see trident.dependencies.

An unknown key, an unknown table, a missing sources list or an unterminated string is reported as an error with the line number, and nothing is built.

Sources

Each entry of sources is a path relative to the directory of the manifest. A * in the last path segment matches files in that one directory, so "models/*.lev" lists every .lev file in models. The matches of one pattern are sorted alphabetically, and the patterns themselves are used in the order you wrote them. The order matters in one case, which is a project without an entry point (below). A listed file that does not exist is an error.

Entry points

The three forms of entry decide which code runs.

  • No entry: script mode. The top-level statements of all the source files run, one file after another in the order of sources. Use it for small programs.
  • A function name, such as entry = "main". The program starts by calling that function. No source file may have top-level statements; all code lives in functions and classes.
  • A file name, such as entry = "main.lev". The top-level statements of that file are the program. The other files may only contain declarations.

trident decides which of the two named forms an entry is: a name that ends in .lev is a file, anything else is a function. It records the decision in the build plan, so the compiler never guesses from a name. A file or function that breaks these rules is a compile error that names the offending statement, for example top-level code outside the entry file.

Script mode: top-level statements run in order

console.writeln("first");
console.writeln("second");
first
second

A function entry looks like this. With entry = "main" and this file as the only source, trident run prints started in main:

A function entry point

void main() {
    console.writeln("started in main");
}

Assets

assets declares the files that the program is allowed to read at compile time with import(path). Each item is a literal path, a glob, or a recursive glob such as "views/**" that matches a whole tree. Assets are paths relative to the manifest. trident hashes each one and records it in the plan. A pattern that matches no files is a warning; a literal path that does not exist is an error. A program that imports a file that is not declared fails to compile with a message that says to add it to assets.

A bare source file with no manifest is a project of one file, so the compiler's project questions (--imports, --graph, --namespaces, --why; see lang.compiler-cli) work on it as well.

Rules

  • The file is named trident.toml and sits at the project root.
  • sources is required and must name at least one file.
  • Keys other than name, entry, sources, assets, version and out are errors, and so are tables other than [[dep]].
  • An entry ending in .lev is a file entry; anything else is a function entry.
  • With a file entry, only that file may have top-level statements. With a function entry, no file may.
  • trident add, remove and update rewrite the whole manifest. Comments and layout are not preserved.

Examples

A project with a named output, several source folders and a function entry:

name    = "notes"
entry   = "main"
sources = ["main.lev", "models/*.lev", "util/*.lev"]
out     = "notes-cli"

A project that embeds its HTML templates:

name    = "site"
entry   = "main.lev"
sources = ["main.lev"]
assets  = ["views/**", "style.css"]

Notes

  • A sources pattern with ** does not search directories recursively; list each directory you want. ** has that meaning only in assets.
  • A namespace does not have to match the directory its file sits in. If you want that convention checked, leviathan --lint-namespaces reports every file whose folder is not the end of the namespace it opens. It is opt-in and no build applies it.

See also

  • Two tools: trident and leviathan — How the package manager and the compiler divide the work, what the build plan between them is, and the commands that build and run a project.
  • Dependencies — Declaring local and repository dependencies, renaming a dependency's namespace with as, development-only dependencies, and the rule that you may only use what you declared.
  • The leviathan command line — Every option of the leviathan compiler, grouped by what it does, with the exit statuses and how arguments reach your program.
  • import() — comptime file inclusion — std::import(path) reads a project file at compile time and folds to its contents as a comptime string.
  • Namespaces — Group declarations under a name, reopen a namespace to add to it, nest namespaces, and reach members with ::.