LEVIATHAN v962456e · 962456eee1

trident

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.

since 0.1.0-alpha.1linux

Description

A dependency is another project that your project uses. You declare it with a [[dep]] table in trident.toml, and trident finds its sources and hands them to the compiler along with your own. The compiler sees one program made of all the files; a dependency is simply more source.

[[dep]]
path    = "../jsonlib"
as      = "Json"
version = "1.0.0"
dev     = false
Key Meaning
path Where the dependency is. A directory next to your project (a local dependency), or the name of a repository (a repository dependency). Required.
version Required for a repository dependency, optional for a local one.
as A namespace name through which your code reaches the dependency; see below. Optional.
dev true for a dependency only needed during development. Optional; default false.

Local and repository dependencies

trident decides which kind a path is by looking at it. If it is a relative path to an existing directory, resolved against the directory of your trident.toml, the dependency is local: that directory must contain its own trident.toml, and its sources and its own dependencies are gathered into your build. Any other path, including an absolute path, is the name of a repository such as github.com/acme/json, and it must have a version; the dependency is fetched from its git repository at that version tag. Versions, the lock file and the checksum record belong to repository dependencies and are described in trident.versions-and-integrity.

A path that is meant to be local but does not exist is read as a repository name, so a typo shows up as the error is not a local directory, so it is treated as a VCS dependency, which requires 'version'.

Using a dependency's code

A dependency declares its code in namespaces, and you bring the namespaces you want into scope with uses, as with your own code. If the dependency's namespace is called JsonLib, your files write uses JsonLib;.

The as key gives you a name of your choosing: trident creates a namespace with that name that brings in everything the dependency declares. With as = "Json" your file can write uses Json; and call the dependency's functions by their bare names, without knowing what the dependency calls its own namespaces. A dependency's namespaces can still be named directly.

This program stands for a project and its dependency after trident has gathered both into one build. The JsonLib namespace is what the dependency's json.lev contributes, and the last three lines are the project's own main.lev:

What a dependency contributes

namespace JsonLib {
    string render(int n) => "{\"n\": ${n}}";
}

uses JsonLib;
console.writeln(render(1));
console.writeln(JsonLib::render(2));
{"n": 1}
{"n": 2}

Development-only dependencies

dev = true marks a dependency that the shipped program does not need, such as a test helper. For a repository dependency, trident build, run and emit-llvm leave it out entirely, while check and plan include it. For a local dependency the flag is recorded but every command still includes it.

You may only use what you declared

A file may uses namespaces from its own project and from the dependencies you declared directly. Reaching into a dependency of a dependency is a compile error, even though the code is present in the build. It would make your project depend on something you never declared and cannot control:

error: namespace 'Inner' comes from an indirect dependency not declared by this project; declare it as a direct dependency to use it

If your project needs Inner, add it as a dependency of its own. trident records which project each source file belongs to and which dependencies that project declared, and the compiler checks the uses statements against that.

Rules

  • A relative path that names an existing directory is a local dependency and needs its own trident.toml. Any other path is a repository dependency and needs a version.
  • A dependency's own dependencies are resolved and gathered too.
  • A file may only uses namespaces declared by its own project or by a direct dependency. A namespace that is only reachable through another dependency is an error.
  • as adds a namespace that brings in the dependency's namespaces. It does not remove the original names.
  • trident add, remove and update edit the [[dep]] tables for you; see trident.commands.

Examples

A project with one local dependency and one repository dependency that is only needed for tests:

name    = "app"
entry   = "main.lev"
sources = ["main.lev"]

[[dep]]
path = "../jsonlib"
as   = "Json"

[[dep]]
path    = "github.com/acme/testkit"
version = "1.4.0"
dev     = true

With that manifest, trident run builds the project with jsonlib only, and trident check also brings in testkit.

Notes

  • To see which namespaces each file declares, uses and can see, run leviathan --imports --plan build/plan.lvplan after trident plan (see lang.compiler-cli).

See also

  • 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.
  • Versions, the lock file, and integrity — How trident picks versions of repository dependencies, what trident.lock and the checksum record guarantee, and how to build without the network.
  • 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.
  • Imports — uses and use — Bring a namespace's names into scope in bulk with uses, or one name at a time with use ... as ....